一、vue3源码中为什么Proxy需要搭配Reflect来实现响应式?
1、reactive的核心逻辑如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| import { isObject } from '@vue/shared' const mutableHandlers = { get(target, key, receiver) { return Reflect.get(target, key, receiver) }, set(target, key, value, receiver) { Reflect.set(target, key ,value, receiver) return true } } export function reactive(target) { if (!isObject(target)) return const proxy = new Proxy(target, mutableHandlers) return proxy }
|
2、在get和set中,使用了Reflect的get,set方法,那为什么不直接用target[key]呢,效果不是一样的么?
看起来是这样,但是在一些情况下,就能看到明显的问题。我们先举个例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| let obj = { name: 'zhangsan', get nickName{ return 'nickName:' + this.name } } // obj中的nickName通过this调用了name,而导致name没有被依赖收集(详情看下面解释) let proxyObj = new Proxy(obj, { get(target, key, receiver) { console.log('收集依赖:', key) return target[key] } }) // 进行取值操作 console.log(proxyObj.nickName)
|
上述代码中,是一个很简单的代理,如果我们在页面中,使用了proxyObj.nickName这个取值代码,那么根据相应逻辑,执行代码打印的结果就是:
1 2
| 收集依赖: nickName nickName:zhangsan
|
那么很明显的问题就是,obj中的name属性,没有被依赖收集,那么如果在后续操作中,我们对proxyObj.name = ‘xxxxxx’进行赋值了,因为没有被依赖收集到,所以虽然数据变化了,但是页面视图却并没有同步发生变化。说到底还是因为this指向的原因,当前this指向了obj,而我们希望这个this指向被代理后的proxyObj,这样才能够将name属性也收集到,那么所以,我们此时应该使用Reflect,来使this正确的指向被代理后的proxyObj属性。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| let obj = { name: 'zhangsan', get nickName() { return 'nickName:' + this.name } } let proxyObj = new Proxy(obj, { get(target, key, receiver) { console.log('收集依赖:', key) return Reflect.get(target, key, receiver) } }) // 进行取值操作 console.log(proxyObj.nickName)
|
经过此番修改,我们再执行代码,会发现,诶name属性也被成功的进行依赖收集了,达到了我们的预期.这就是为什么这里要使用Reflect的原因啦。
1 2 3
| 收集依赖: nickName 收集依赖: name nickName:zhangsan
|
3、详细了解Reflect
见ES6+/《ES6中的Reflect》