vue3(更新中...)

99 阅读9分钟

1.setup选项

  1. setup 选项的执行时机?
    beforeCreate钩子之前 自动执行

  2. setup写代码特点是什么?
    定义数据+函数 然后以对象的方式return

  3. < script setup > 解决了什么问题?
    经过语法糖的封装更简单的使用组合式api

  4. setup中的this 还指向组件的实例吗?
    指向undefined

2.reactive 和 ref 函数

reactive()

接受对象类型数据的参数传入并返回一个响应式的对象

<template>
   <button @click="addCount">{{state.count}}</button>
</template>

<script setup>
//导入函数
import {reactive} from "vue";
// 执行函数 传入一个对象类型的参数 变量接收
  const state=reactive({
     count:0
  })
  const addCount=()=>{
       state.count++
  }
</script>

ref() 接受简单类型或者对象类型的对象传入并返回一个响应式的对象

<template>
   <button @click="addCount">{{state1}}</button>
</template>

<script setup>
//导入函数
import {reactive,ref} from "vue";
// 修改ref产生的响应式对象数据,必须要通过.value 属性
  const state=reactive({
     count:0
  })
const state1=ref(0)
  const addCount=()=>{
       state.count++
       state1.value++
  }
</script>
  1. reactive 和 ref 函数的共同作用是什么?
    用函数调用的方式生成响应式数据
  2. reactive vs ref?
  • 1.reactive 不能处理简单类型的数据
  • 2.ref 参数类型支持更好但是必须通过.value访问
  • 3.ref的内部实现依赖于reactive函数
  1. 在实际工作中更推荐哪个?
    推荐使用ref 函数,更加灵活

computed 计算属性函数

计算属性和vue2的完全一致,组合式API的计算属性只是修改了写法

<template>
   <div>{{list}}</div>
   <div>{{shaxuan}}</div>
</template>
<script setup>
// 导入computed
import {computed, ref} from 'vue'
 const list=ref([1,2,3,4,5])
// 执行函数 return 计算之后的值 变量接收
 const shaxuan=computed(()=>{
     return list.value.filter(item=>item>2)
 })
setTimeout(()=>{
    list.value.push(9,0)
},2000)
</script>
  1. 计算属性中不应该有“副作用” 比如异步请求/修改dom
  2. 避免直接修改计算属性的值 计算属性应该是只读的

watch 函数

作用:侦听一个或多个数据的变化,数据变化时执行回调函数 两个额外的参数:1.immediate(立即执行) 2.deep(深度监听)

在watch中 ref 不需要加.value

单个监听

<template>
   <button @click="add">
      {{data}}
   </button>
</template>
<script setup>
import {ref,watch} from 'vue'

const data=ref(23)
const add=()=>{
    data.value++
}
watch(data,(newVal,oldVal)=>{
    console.log(newVal,oldVal,'111')
})
</script>

多个监听 说明:同时监听多个响应式数据的变化,不管哪个数据变化都需要执行回调

<template>
    <button @click="addCount">
       修改count {{count}}
    </button>
  <button @click="addPc">
      修改 name {{pc}}
  </button>
</template>
<script setup>
import {ref,watch} from 'vue'

const count=ref(23)
const pc=ref('pc')
const addCount=()=>{
    count.value++
}
const addPc=()=>{
     pc.value='cp'
}
// 同时监听多个值的使用  都是采用数组的形式
watch([count,pc],([newCount,newPc],[oldCount,oldPc])=>{
    console.log(newCount,newPc,oldCount,oldPc,'new')
})

</script>

immediate 说明:在监听器创建时 立即触发watch一次

// 同时监听多个值的使用  都是采用数组的形式
watch([count,pc],([newCount,newPc],[oldCount,oldPc])=>{
    console.log(newCount,newPc,oldCount,oldPc,'new')
},{
  immediate:true
})

deep 默认机制:通过watch监听的ref对象默认是浅层监听的,直接修改嵌套的对象属性不会触发回调执行,需要开启deep选项

// 同时监听多个值的使用  都是采用数组的形式
watch([count,pc],([newCount,newPc],[oldCount,oldPc])=>{
    console.log(newCount,newPc,oldCount,oldPc,'new')
},{
  deep:true
})

精确监听对象的某个属性 在不开启deep的前提下,监听age的变化,只有age变化时才执行回调

const info=ref({
name:'cp',
age:18
})

第一个参数是一个函数,指向需要监听的那个数据,不需要开启deep

watch(()=>state.value.age,(newVal)=>{
   console.log(newVal,'newVal')
})

deep 性能损耗,尽量不开启deep

  1. 作为watch函数的第一个参数,ref对象需要添加.value吗?
    不需要,watch会自动读取
  2. watch只能监听单个数据吗?
    单个或多个
  3. 不开启deep,直接修改嵌套属性能触发回调吗?
    不能,默认是浅层监听
  4. 不开启deep,想着某个层次比较深的属性变化时执行回调怎么做?
    可以把第一个参数写成函数的写法,返回要监听的具体属性

生命周期函数

image.png

生命周期函数是可以执行多次的,多次执行时传入的回调会在时机成熟时依次执行

<template>
  <button></button>
</template>
<script setup>
// 导入函数
import {onMounted} from "vue";
onMounted(()=>{
   console.log(2323232)
})
onMounted(()=>{
  console.log(11111)
})
</script>
  1. 组合式ApI中生命周期函数的格式是什么?
    on+生命周期名字
  2. 组合式ApI中可以使用onCreated吗?
    没用这个钩子函数,直接写到setup中
  3. 组合式API中组件卸载完毕时执行哪个函数?
    onUnmounted

组合式API 父子通信

基本思想 1.父组件中给子组件绑定属性 2.子组件内部通过props选项接收

父组件:

<template>
  <div>父组件aPP</div>
  <AboutView message="this is a message"></AboutView>
</template>
<script setup>
// setup 语法糖下局部组件无需注册直接可以使用
import AboutView from "@/views/AboutView.vue";
</script>

子组件:

<template>
  <div class="about">
    <h1>This is an about page</h1>
     <div>11{{message}}</div>
  </div>
</template>
<script setup>
// defineProps 接收数据
  const props = defineProps({
        message:String
   })
  console.log(props.message,'props')
</script>

组合式API 子传父

基本思想: 1.父组件中给子组件标签通过@绑定事件 2.子组件内部通过$emit方式触发事件

父组件:

<template>
  <div>父组件aPP</div>
   <!--  定义自定义事件-->
  <AboutView message="this is a message" :count="count" @get-message="getMessage"></AboutView>
</template>
<script setup>
import {ref} from 'vue'
// setup 语法糖下局部组件无需注册直接可以使用
import AboutView from "@/views/AboutView.vue";
const count=ref(100)
const getMessage=(msg)=>{
   console.log(msg,'msg')
}
</script>

子组件:

<template>
  <div class="about">
    <h1>This is an about page</h1>
     <div>11{{message}}</div>
  </div>
</template>
<script setup>
  const props = defineProps({
        message:String,
        count:Number
   })
  // 通过 defineEmits 编译成 emit 方法   是个数组的形式,可以同时传多个方法名
  const emit= defineEmits(['get-message'])
  // 触发自定义事件,并传递参数
  emit('get-message','this is a apple')
  console.log(props.message,props.count,'props')
</script>
  1. 父传子的过程中通过什么方式接收props?
    defineProps({属性名:类型})
  2. setup语法糖中如何使用父组件传过来的数据?
    const props=defineProps({属性名:类型})
  3. 子传父的过程中通过什么方式得到emit方法?
    defineEmits(["事件名称"])

组合式API 模版引用

模版引用的概念:通过ref标识获取真实的dom对象或者组件实例对象 vue3中如果父组件想要调用子组件的属性和方法需要使用defineExpose,在vue3中子组件的属性和方法是封闭的

<template>
   <div>
       <!-- 通过ref 标识绑定对象-->
       <div ref="count">我要吃苹果</div>
   </div>
</template>
<script setup>
import {onMounted, ref} from 'vue'
   // 调用ref函数 -> ref 对象
   const count=ref(null)
   // 组件挂载完毕后才能获取
   onMounted(()=>{
       console.log(count.value,'value22')
   })
   defineExpose({ count })
</script>
  1. 获取模版引用的时机是什么?
    组件挂载完毕

  2. defineExpose编译宏的作用是什么?
    显式暴露组件内部的属性和方法

project 和 inject

作用:顶级组件向任意的底层组件传递数据和方法,实现跨层组件通信 1.顶层组件通过provide函数提供数据 2.底层组件通过inject函数获取数据

頂級組件:

<template>
  <AboutView></AboutView>
</template>
<script setup>
import AboutView from "@/views/AboutView.vue";

import {provide,ref} from "vue";
// 顶部组件提供数据
provide('get-message','hello-world')

// 传递响应式数据  key 需要设置成不一样的
const count=ref(100)
provide('key-message',count)

setTimeout(()=>{
   count.value=300
},2000)
</script>

中间组件:

<template>
<hello-view></hello-view>
</template>
<script setup>
import HelloView from "@/views/HelloView.vue";
</script>

底层组件:

<template>
   <div>底部组件 {{msg}}{{count}}</div>
</template>
<script setup>
import {inject} from "vue";
// 底部组件接收数据
const msg=inject('get-message')

const count=inject('key-message')
</script>

跨层传递方法 顶级组件可以向底层组件传递方法,底层组件调用方法修改顶层组件中的数据

顶级组件:

<template>
  <AboutView></AboutView>
</template>
<script setup>
import AboutView from "@/views/AboutView.vue";

import {provide,ref} from "vue";
// 顶部组件提供数据
provide('get-message','hello-world')

// 传递响应式数据  key 需要设置成不一样的
const count=ref(100)
provide('key-message',count)

// 传递方法(单项数据流,谁的数据谁负责修改,底层组件使用顶层组件的方法修改数据)
const setCount=()=>{
   count.value++
}
provide('set-count',setCount)

setTimeout(()=>{
   count.value=300
},2000)
</script>

中间组件:

<template>
<hello-view></hello-view>
</template>
<script setup>
import HelloView from "@/views/HelloView.vue";
</script>

底层组件:

<template>
   <div >底部组件 {{msg}}{{count}}</div>
  <button @click="setCount">增加</button>
</template>
<script setup>
import {inject} from "vue";
// 底部组件接收数据
const msg=inject('get-message')

const count=inject('key-message')
// 底部组件调用方法
const setCount=inject('set-count')
</script>

使用场景: 多层嵌套时使用到,底层组件写好评价后需要通知顶级组件,可以利用顶级组件向底层组件传递方法的形式获取方法来通知顶级组件

  1. provide 和 inject 的作用是什么?
    跨层组件通信

  2. 如何在传递的过程中保持数据响应式?
    第二个参数传递ref对象

  3. 底层组件想要通知顶层组件做修改,如何做?
    传递方法,底层组件调用方法

  4. 一颗组件树中只有一个顶层或底层组件吗?
    相对概念,存在多个顶层和底层的关系

Pinia

Pinia是vue的专属的最新状态管理库,是vuex状态管理工具的替代品

  1. 提供更加简单的API(去掉了mutation)
  2. 提供符合组合式风格的API(和 vue3的新语法统一)
  3. 去掉了modules的概念,每一个store都是一个独立的模块
  4. 搭配TypeScript 一起使用提供可靠的类型推断
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
 // 导入createPinia
import {createPinia} from 'pinia'
// 执行方法得到实例
const pinia=createPinia()
// 把pinia实例放入到app应用中

createApp(App).use(pinia).use(store).use(router).mount('#app')

使用Pinia实现 计数器案例

getters 实现 Pinia中的getter直接使用 computed函数进行模拟

Pinia 内部

// 导入一个方法defineStore
import {defineStore} from "pinia";
import {ref} from 'vue'
export const useCounterStore=defineStore('counter',()=>{
     // 定义数据(state)
    const count=ref(0)
    // 定义修改数据的方法(action 同步+异步)
    const increment=()=>{
         count.value++
    }
    // 以对象的方式return 供组件使用
    return {count,increment}
})
<template>
    <button @click="counterStore.increment()">{{counterStore.count}}</button>
</template>
<script setup>
// 导入use 打头的方法
import {useCounterStore} from '@/store/counter'
// 执行方法得到store实例对象
const counterStore = useCounterStore()
</script>
<style>
</style>

Pinia 解构赋值 普通的变量解构赋值需要借助 storeToRefs 方法的解构赋值直接从store实例对象 中 直接解构

<template>
        <button @click="increment()">{{count}}</button>
        {{doubleCount}}
        <ul>
          <li v-for="(item,index) in list" :key="index">{{item.name}}</li>
        </ul>
</template>
<script setup>
// 导入use 打头的方法
import {useCounterStore} from '@/store/counter'
import {onMounted} from "vue";
import {storeToRefs} from "pinia";

// 执行方法得到store实例对象
const counterStore = useCounterStore()

// 方法包裹(保持响应式更新)
const {count,doubleCount,list} =storeToRefs(counterStore)

// 方法直接从原来counterStore中解构赋值
const {increment} =counterStore



onMounted(()=>{
  counterStore.getList()
})
</script>
<style>
</style>
  1. Pinia 是用来做什么的? 集中状态管理工具,新一代vuex
  2. Pinia 中还需要mutation吗? 不需要,action 既支持同步也支持异步
  3. Pinia 如何实现getter? computed 计算属性
  4. Pinia 产生的store 如何解构赋值保持响应式? storeToRefs 只管数据,不能解构赋值方法