Vue3 响应式数据的原理

74 阅读3分钟

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)

上面代码的具体流程为:

  1. 它会先调用Proxy
  2. 这个Proxy再调用产品
  3. 然后返回到Proxy
  4. 返回这个product到控制台日志
  5. 最后就会打印出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