CodeWalk

Vue 3 的响应式原理(Proxy)与 Vue 2(defineProperty)的区别

作者:我还是少年 · 2026-05-30 12:55

请说明 Vue 3 基于 Proxy 的响应式系统相比 Vue 2 基于 Object.defineProperty 的改进。

回答

我还是少年

Vue 2 响应式(Object.defineProperty)

Object.defineProperty(obj, key, {
  get() { track(obj, key); return value; },
  set(newVal) { value = newVal; trigger(obj, key); }
});

缺陷

  1. 无法检测新增/删除属性:需 Vue.set() / Vue.delete()
  2. 数组拦截不完整:不能检测索引赋值和 length 变化(需重写 7 个数组方法)
  3. 递归遍历:初始化时深度递归,性能开销大
  4. 只能代理对象:Map/Set 无法响应式

Vue 3 响应式(Proxy)

const handler = {
  get(target, key, receiver) {
    track(target, key);
    return Reflect.get(target, key, receiver);
  },
  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);
    return result;
  },
  has(target, key) { /* ... */ },
  ownKeys(target) { /* ... */ }
};

改进点

特性Vue 2 (defineProperty)Vue 3 (Proxy)
新增属性❌ 需 Vue.set✅ 自动检测
删除属性❌ 需 Vue.delete✅ 自动检测
数组操作❌ 需重写方法✅ 原生支持
Map/Set❌ 不支持✅ 支持
初始化开销高(递归遍历所有属性)低(惰性代理,访问时再递归)
性能中等更好(只代理对象本身)
类型检查__ob__ 标记无需标记

注意:Vue 3 的 ref() 对于基本类型也使用 getter/setter 包装,而 reactive() 直接使用 Proxy。