Skip to content

Proxy

处理 target[key] 无法处理依赖收集的问题

Vue 3 的响应式系统基于 Proxy 实现。在使用 Proxy 代理对象时,如果对象内部存在依赖 thisgetter 等访问逻辑,就需要特别关注 this的指向问题。例如:

ts
const person = {
  name: "soppylzz",
  get greet() {
    return `my name is ${this.name}`
  },
}

const personProxy = new Proxy(person, {
  get(target, key, recevier) {
    return target[key] // this -> person
  },
})

console.log(personProxy.greet)

这段代码表面看起来没有问题,但实际执行 target[key] 时,greetgetter 会以 target(即原始对象 person)作为调用者执行。此时 this === person,访问的是 person.name,而非代理对象上的属性。由于 name 的访问并未经由 Proxy,也就无法触发依赖收集,这显然不符合 Vue 3 响应式系统的设计预期。

一个直观的改进思路是改为返回 receiver[key],但这样会再次触发当前代理对象的 get 拦截,导致无限递归,最终引发栈溢出。为此,Vue 3 源码中没有直接使用 target[key],而是借助 Reflect.get 来解决:

ts
const mutableHandler: ProxyHandler<any> = {
  get(target, key, receiver) {
    return Reflect.get(target, key, receiver)
  },
}

Reflect.get 的三个参数含义如下:

参数含义
target被读取属性的目标对象
key要读取的属性名
receiver当读取的是 getter 属性时,用作 getter 内部的 this 指向

使用 Reflect.get 后,外层读取 greet 时仍以 person 为属性来源,不会触发无限递归;而 getter 内部的 this 则指向 personProxy,从而对 name 的访问也能被拦截,为后续的依赖收集打下正确基础。