Vue3 Proxy 响应式原理

Vue3 如何通过 Proxy 实现响应式,以及 track/trigger 机制。

#type / concept #status / growing #tech / dev / frame #resource / vue3

[!info] related notes

Vue3 Proxy 响应式原理

一句话定义

Vue3 用 Proxy 代理整个对象,在运行时拦截 get/set/deleteProperty 等操作,通过 track 收集依赖、trigger 触发更新。

核心内容

Proxy 代理整个对象

function reactive(target) {
  return new Proxy(target, {
    get(target, key, receiver) {
      track(target, key)
      const result = Reflect.get(target, key, receiver)
      return isObject(result) ? reactive(result) : result
    },
    set(target, key, value, receiver) {
      const result = Reflect.set(target, key, value, receiver)
      trigger(target, key)
      return result
    },
    deleteProperty(target, key) {
      const result = Reflect.deleteProperty(target, key)
      trigger(target, key, 'DELETE')
      return result
    }
  })
}

依赖存储结构:WeakMap → Map → Set

const targetMap = new WeakMap()
// 结构:
// targetMap:
//   target1 (原始对象) -> Map:
//     'name' -> Set([effect1, effect2])
//     'age'  -> Set([effect3])
//   target2 -> Map:
//     'count' -> Set([effect4])

track:收集依赖

function track(target, key) {
  if (!activeEffect) return
  
  let depsMap = targetMap.get(target)
  if (!depsMap) {
    depsMap = new Map()
    targetMap.set(target, depsMap)
  }
  
  let dep = depsMap.get(key)
  if (!dep) {
    dep = new Set()
    depsMap.set(key, dep)
  }
  
  dep.add(activeEffect)
}

trigger:触发更新

function trigger(target, key, type) {
  const depsMap = targetMap.get(target)
  if (!depsMap) return
  
  const effects = depsMap.get(key)
  if (effects) {
    effects.forEach(effect => effect())
  }
}

effect:副作用函数

function effect(fn) {
  const wrappedFn = () => {
    activeEffect = wrappedFn
    fn()
    activeEffect = null
  }
  wrappedFn() // 首次执行,收集依赖
  return wrappedFn
}

边界与易混淆点

为什么配合 Reflect

  • 语义标准Reflect 是与 Proxy 配套的官方 API
  • 返回值合理Reflect.set 返回布尔值表示设置是否成功
  • 原型链准确receiver 参数保证 this 指向正确

Vue3 能监听的操作

操作Proxy 能拦截
属性读取✅ get
属性赋值✅ set
属性删除✅ deleteProperty
arr[index] = value✅ set
arr.length = 0✅ set
Map.set / Map.get✅ get / set
新增属性✅ set

和 Vue2 Object.defineProperty 的区别

  • Vue2:初始化时递归每个属性预先劫持
  • Vue3:代理整个对象,访问时再处理,惰性更高

ref 为什么还需要

Proxy 只能代理对象,不能代理原始值。ref 把基本类型包装成对象:

const count = ref(0)
// 内部类似:
// { value: 0 },然后对 value 做响应式处理
创建于 2026/4/7 更新于 2026/5/27