Skip to content

Reflect 与 Vue 3 响应式

Reflect 是 ES6 引入的一个内置对象,它提供了一系列拦截 JavaScript 操作的方法。在 Vue 3 中,ReflectProxy 配合使用,是实现响应式系统的关键。

1. 为什么 Vue 3 需要 Reflect?

你可能会问,Proxy 已经可以拦截操作了,为什么还需要 Reflect

核心原因在于:修正 this 指向,确保依赖收集的正确性。

场景重现:Getter 中的 this 问题

假设我们有一个对象,其中一个属性的 getter 依赖于另一个属性:

javascript
const person = {
  name: 'Jack',
  get alias() {
    // 这里的 this 指向谁?
    return 'Handsome ' + this.name
  }
}

如果我们使用 Proxy 代理这个对象,但只使用普通的 target[key] 来返回值:

javascript
const proxy = new Proxy(person, {
  get(target, key, receiver) {
    console.log(`读取了 ${key}`)
    return target[key] // <--- 问题出在这里
  }
})

当我们访问 proxy.alias 时:

  1. get 拦截器被触发,key 为 alias
  2. 返回 target['alias'],即调用原始对象 person 的 getter 方法。
  3. 关键点:在 getter 方法内部,this 指向的是 target (即原始对象 person),而不是 proxy
  4. getter 执行 this.name,即 person.name
  5. 后果person.name 的访问不会触发 Proxy 的 get 拦截器。这意味着 Vue 无法追踪到 alias 依赖于 name

使用 Reflect 解决

Reflect.get(target, key, receiver) 允许我们显式传入 receiver 作为 this 的指向。

javascript
const proxy = new Proxy(person, {
  get(target, key, receiver) {
    console.log(`读取了 ${key}`)
    // 使用 Reflect,将 receiver (也就是 proxy 实例) 传递进去
    return Reflect.get(target, key, receiver)
  }
})

现在当我们访问 proxy.alias 时:

  1. get 拦截器触发,key 为 alias
  2. 调用 Reflect.get,并传入 receiver (即 proxy)。
  3. getter 执行,此时内部的 this 指向 receiver (即 proxy)。
  4. 执行 this.name,即 proxy.name
  5. 成功proxy.name 再次触发 get 拦截器。Vue 成功收集到依赖:alias -> name

2. Reflect 的其他优势

除了修正 this 指向,Reflect 还有以下好处:

(1) 统一的对象操作 API

很多对象操作是分散的,比如 delete obj.propkey in objReflect 将它们统一为函数调用:

  • Reflect.deleteProperty(obj, prop)
  • Reflect.has(obj, key)

(2) 返回布尔值状态

传统的 Object.defineProperty 在失败时会抛出错误,需要 try-catch。而 Reflect.defineProperty 会返回布尔值,代码更健壮:

javascript
// 旧方式
try {
  Object.defineProperty(target, property, attributes)
} catch (e) {
  // 处理错误
}

// 新方式
if (Reflect.defineProperty(target, property, attributes)) {
  // 成功
} else {
  // 失败
}

(3) 与 Proxy Handler 方法一一对应

Reflect 的所有静态方法(13个)和 Proxy 的 handler 方法是一一对应的。这使得在 Proxy 内部调用原始行为变得非常简单且规范。

javascript
const handler = {
  get(target, key, receiver) {
    track(target, key) // Vue 的依赖收集
    return Reflect.get(target, key, receiver) // 执行默认行为
  },
  set(target, key, value, receiver) {
    const result = Reflect.set(target, key, value, receiver) // 执行默认行为
    trigger(target, key) // Vue 的派发更新
    return result
  }
}

3. 总结

在 Vue 3 的响应式系统中,Reflect 的主要作用是:

  1. 配合 Proxy 使用:在拦截器中完成默认行为。
  2. 修正 this 指向:确保 getter 中访问的属性也能触发 Proxy 拦截,从而实现正确的依赖收集。