1. 拉开序幕的 setup
-
理解:vue3 中的一个新的配置项,值为一个函数。
-
setup 是所有 Compositon api 的表演的舞台。
-
组件中用到的:数据,方法,watch,计算属性等都要配置在 setup 中
-
setup 的返回值有两种:
- 若返回一个对象,则对象中的属性,方法,在模板中均可以直接使用。(重点关注)
- 若返回的是一个渲染函数,则可以直接定义渲染内容。(了解)
- 注意点:
-
尽量不要与 vue2 混用。
-- vue2配置(data, methods, computed...)中可以访问到 setup 中的属性,方法。但是setup 中不能访问到 vue2 中的配置(data, methods, computed...)
-- 如果有重名,setup 优先 -
setup 不能是一个 async 函数,因为返回值不再是 return 的对象,而是 promise, 模板看不到 returen 对象中的属性。
2. ref函数
- 作用:定义一个响应式的数据。
- 语法: const xxx = ref(initeValue)
- 创建一个包含响应式数据的引用对象(reference 对象)。
- JS 中操作数据: xxx.value
- 模板中读取不用 .vulue 直接写。
- 备注:
- 接收数据可以是是基本数据类型,也可以是对象类型。
- 基本数据类型:响应依然是靠 Object.defineProperty() 里的 get 和 set 完成的。
- 对象类型:内部是求助了 vue3 中的内置的新函数 reactive 函数。
3. reactive 函数
- 作用:定义一个对象类型的响应式。基本理性用 ref 函数。
- 语法: const 代理对象 = reactive(原对象)。 接收一个对象(或者数据),返回一个代理对象(proxy 对象)。
- reactive 定义的响应式数据是深层次的。
- 内部基于 ES6 的 proxy 实现,通过代理对象操作源对象内部数据,进行操作。
4. vue2 的响应式原理
- 实现原理:
- 对象类型:通过 Object.defineProperty() 对属性的读取,修改进行拦截(数据劫持)。
- 数据类型:通过重写更新数组的一系列方法来实现拦截. (对数组的变更方法进行了包裹)。
Object.defineProperty(data,'count',{
set(){},
get(){}
})
- 存在问题:
- 新增属性,删除属性,界面不会发生变化
- 数组通过下标修改,不会更新界面
5. vue3 的响应式
- 实现原理:
- 通过 Proxy(代理):拦截对象中任意属性的变化,包括属性的读写,添加,删除等。
- 通过 Reflect (反射):实现源对象的属性进行操作
- MDN 有对应的文档
new Proxy(data, {
//拦截读取数据属性值
get(target, prop){
return Reflect.get(target,prop)
},
//拦截设置或或添加属性值
set(target,prop,value){
return Reflect.set(target,prop,value)
}
//拦截删除属性
deleteProperty(target, prop){
return Reflect.deleteProperty(target, prop)
}
})
6. ref 和 reactive 的对比
- 从定义数据角度对比
- ref 定义基本类型数据
- reactive 定义对象或者数组类型数据
- 备注:ref 也可以定义对象或者数组类型数据,但是内部它自动通过 reactive 转为代理对象实现。
- 从原理角度对比:
-
ref 还是通过 vue2 的 Object.defineProperty() 的 get 和 set 实现的响应式(数据劫持)。
-
reactive 通过使用 Proxy 来实现响应式(数据劫持),并通过 Reflect 操作源对象内部的数据。
- 从使用角度对比:
-
ref 定义的数据:操作数据需要 .value。 读取数据时候模板中直接读取不需要 .value
-
reactive 定义的数据,操作与读取均不需要 .value
7. setup 的两个注意点
-
setup 的执行时机
在 beforeCreate 之前执行一次,this 是 undifined -
setup 的参数
- props:值为对象,包含:组件外部传递过来,且组件内部声明接收了的属性。
- context:上下文对象
-- attrs:值为对象,包含:组件外部传递过来,但是没有在 props 配置中-声明的属性。相当于 this.slots.
-- emit: 分发自定义函数,相当于 this.$emit.
8. 计算属性与监视
8.1 computed 函数
- 与vue2 中的 computed 相比,配置功能一致。
- 写法
import {computed} from 'vue'
setup(){
...
//计算属性一般写法
let fullName = computed(()=>{
return person.firstName + '-' + person.lastName
})
//计算属性完整写法
let fullName = computed(()=>{
get(){
return person.firstName + '-' + person.lastName
},
set(value){
const nameArr = value.splice("-")
person.firstName = nameArr[0]
person.lastName = nameArr[1]
}
})
}
8.2 watch 函数
- 与 vue2 中的 watch 配置一样
- 两个小坑
- 监视 reactive 定义的响应式数据时,oldValue 无法正确获取,强制开启了深度监视(deep 配置失效)。
- 监视 reactive 定义的响应式数据中的某个属性时候,deep 配置有效。
//情况一,监视 ref定义的数据
watch(sum, (newValue,oldVaule)=>{
consolo.log("sum数据变化了", newValue,oldVaule)
})
//情况二,监视多个 ref定义的数据
watch([sum,msg], (newValue,oldVaule)=>{
consolo.log("sum或者 msg 数据变化了", newValue,oldVaule)
})
//情况三,监视reactive定义的数据
//若 watch 监视的是 reactive 定义的响应式数据,则无法获取正确的 oldValue
//若 watch 监视的是 reactive 定义的响应式数据,则强制开启了深度监视
watch(person, (newValue,oldVaule)=>{
consolo.log("person数据变化了", newValue,oldVaule)
}, {immediate:ture, deep: false}) //此处的 deep 不生效
//情况四,监视reactive定义的某个属性
watch(()=>person.job, (newValue,oldVaule)=>{
consolo.log("person的job数据变化了", newValue,oldVaule)
})
//情况五,监视reactive定义的某些属性
watch([()=>person.job,()=>person.name], (newValue,oldVaule)=>{
consolo.log("person的job或者name数据变化了", newValue,oldVaule)
})
//特殊情况
watch([()=>person.job,()=>person.name], (newValue,oldVaule)=>{
consolo.log("person的job或者name数据变化了", newValue,oldVaule)
}, {deep:true}) //此处由于监视的是 reactive 定义中的某个属性,所以 deep配置有效。
8.3 watchEffect 函数
- watch 函数的套路:既要指明监视的属性,也要指明监视的回调
- watchEffect 函数的套路是:不用指明监视那个属性,监视的回调中用到那个属性,那就监视那个属性。
- watchEffect有点像 computed:
- 但是 computed 注重的计算出来的值(回调函数返回值),所以必须要写返回值。
- 而 watchEffect 更注重的是过程(回调函数的函数体),所以不用写返回值。
//watchEffect所指定的回调中用到的数据只要发生变化,则直接重新执行回调
watchEffect(()=>{
const x = sum.value
const x2 = person.age
console.log("watchEffect配置的回调执行了")
})
网友评论