响应式方案
除了基本数据类型和对象,JavaScript
还有很多比如Map
、Set
一样的数据结构,针对这些数据,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
方法收集到的依赖只有在对象新增和删除属性时才会被重新执行。