Vue3 响应式数据的原理
响应式原理是通过以下几个函数实现者这个整个流程的
- effect 执行函数
- track 收集依赖放在dep中
- trigger 通知dep里所有依赖执行
- dep 放有effect的一个空间
那又如何自动的通知track函数进行依赖收集和自动执行trigger函数去进行通知更新
这里我们就用到vue3的核心,那就是Proxy!
理解ES6 Proxy
Proxy
就是另一个对象的占位符,默认情况下对该对象进行委托。简单来说就是一个对象委托。
例如下面的代码就是完成了b代理了a这个对象
let a = { price: 5, quantity: 2 }
let b = new Proxy(a, {})
console.log(b.quantity)
上面代码的具体流程为:
- 它会先调用
Proxy
- 这个
Proxy
再调用产品 - 然后返回到
Proxy
- 返回这个product到控制台日志
- 最后就会打印出
2
Proxy
中的第二个参数,它叫做处理函数,在这个处理函数中,你可以传递一个诱捕器,它的作用就是可以让我们拦截基本操作,如属性查找
,或枚举
或函数调用
。
处理函数其中最重要的就是get
函数和set
函数
- get函数主要是在访问时触发的函数
- set函数主要是在改变值的时候触发的函数
那在vue3中的reactive函数我们就可以简略的写出来了
function reactive(target) {
const handler = {
//target 原来的对象
//key 属性名
//receiver 代理后的对象
get(target, key, receiver) {
track(receiver, key) // 访问时收集依赖
return Reflect.get(target, key, receiver)
//可以理解target[key].call(receiver)
},
set(target, key, value, receiver) {
Reflect.set(target, key, value, receiver)
trigger(receiver, key) // 设值时自动通知更新
}
}
return new Proxy(target, handler)
}
reactive函数写出来后,定义数据的时候就可以直接把数据直接给代理了,之后数据的调用和改变都会自动的去调用track函数和trigger函数,从而实现响应式数据
但其中还是用到了Reflect这个内置对象,那为什么我们会用Reflect呢?
因为直接用target[key]调用的话,又会应为被调用而重新触发get函数,所以会导致无限循环,而刚好Reflect可以访问的值时receiver的key的值,但不是直接访问receiver[key]属性,所以用Reflext取值不会触发Proxy的触发函数。
那vue3中有了reactive()又为什么要出现ref()呢,它又解决了什么问题呢?
其实reactive有个缺点就是不能去定义普通变量(比如number,string等),Proxy只能代理对象,不能代理普通变量,所以reactive()不能实现对普通变量的响应化
所以ref()的目的就是为了实现普通变量的响应化,原理很简单,就是在reactive上做了一层封装
简单实现起来就是这样
function ref (initValue) {
return reactive({
value: initValue
})
}
所以,被ref响应化后想要拿到具体的值时,就需要在后面加一个value
例如
let num = ref (5)
num.value=10
console.log(num)10