从零开始学习Vue3源码 (五、拾遗)

26 阅读2分钟

一、vue3源码中为什么Proxy需要搭配Reflect来实现响应式?

1、reactive的核心逻辑如下:

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]呢,效果不是一样的么?

看起来是这样,但是在一些情况下,就能看到明显的问题。我们先举个例子:

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这个取值代码,那么根据相应逻辑,执行代码打印的结果就是:

收集依赖: nickName
nickName:zhangsan

那么很明显的问题就是,obj中的name属性,没有被依赖收集,那么如果在后续操作中,我们对proxyObj.name = 'xxxxxx'进行赋值了,因为没有被依赖收集到,所以虽然数据变化了,但是页面视图却并没有同步发生变化。说到底还是因为this指向的原因,当前this指向了obj,而我们希望这个this指向被代理后的proxyObj,这样才能够将name属性也收集到,那么所以,我们此时应该使用Reflect,来使this正确的指向被代理后的proxyObj属性。

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的原因啦。

收集依赖: nickName
收集依赖: name
nickName:zhangsan

3、详细了解Reflect

见ES6+/《ES6中的Reflect》