Vue 3 的响应式原理(Proxy)与 Vue 2(defineProperty)的区别
请说明 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); }
});
缺陷:
- 无法检测新增/删除属性:需
Vue.set()/Vue.delete() - 数组拦截不完整:不能检测索引赋值和 length 变化(需重写 7 个数组方法)
- 递归遍历:初始化时深度递归,性能开销大
- 只能代理对象: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。