响应式方案

除了基本数据类型和对象,JavaScript还有很多比如MapSet一样的数据结构,针对这些数据,Vue3也有独特的处理方案。

# Proxy与Reflect

Proxy能够拦截对于一个对象的读取和设置等基本语义操作,但是Vue3中还用了Reflect来完善响应式。 先总结一下使用Reflect的原因:确保涉及this指向的函数中,this都指向代理对象。

先看看目前的响应式系统

const proxy = new Proxy(obj, {
    get(target, val){
        track(target, val)
        //直接返回target[val]
        return target[val]
    },
    set(target, val, newVal){
        //直接设置target[val]
        target[val] = newVal
        trigger(target, val)
    }
})

大多数情况下在get中直接返回target[key]并没有任何问题,因为这个读取操作可以被代理对象拦截从而进行依赖收集。 但是若obj是这样的对象:

let obj = {
    foo: 1,
    get bar(){
        return this.foo
    }
}

就会出问题,因为这里bar是一个取值操作,它的this默认指向的是被代理的原对象而非代理对象,这样就不能进行正确的依赖收集。 因此使用Reflect来读取数据。

......
get(target, val, reciver) {
    track(target, val)
    return Reflect.get(target, key, reciver)
}
......

reciver就是代理对象,通过Reflect可以保证涉及this的操作指向代理对象,从而进行正确依赖收集。

# 如何代理对象?

Vue在读取对象数据时进行依赖收集。“读取”的概念是比较宽泛的,下面是涉及到读取对象的一些操作, 以及在Proxy中对这些操作进行拦截的方法。

操作 Proxy拦截方法 依赖收集的key值
obj.foo get foo
'foo' in obj has foo
for...in... ownKeys ITERATE_KEY
delete obj.foo deleteProperty /

其中ownKeys方法收集到的依赖只有在对象新增和删除属性时才会被重新执行。

上次更新:: 2023/9/18 15:22:28