Vue 2 使用 Object.defineProperty 实现数据响应式,而 Vue 3 改用 Proxy。两者的核心目标都是实现数据的响应式更新,但实现方式和能力有显著差异。以下是它们的对比和原理详解:
一、Vue 2 的 Object.defineProperty
1. 实现原理
- 对象监听:通过
Object.defineProperty递归地为对象的每个属性添加getter和setter。 - 数组监听:通过重写数组的
push、pop、shift、unshift、splice、sort、reverse方法实现监听。
2. 代码示例
// 简化版数据劫持
function defineReactive(obj, key, val) {
Object.defineProperty(obj, key, {
get() {
console.log(`读取 ${key}: ${val}`);
return val;
},
set(newVal) {
if (newVal !== val) {
console.log(`设置 ${key}: ${newVal}`);
val = newVal;
// 触发视图更新(模拟)
updateView();
}
},
});
}
// 递归监听对象
function observe(obj) {
if (typeof obj !== 'object' || obj === null) return;
Object.keys(obj).forEach((key) => {
defineReactive(obj, key, obj[key]);
});
}
// 测试
const data = { name: 'Alice' };
observe(data);
data.name = 'Bob'; // 输出 "设置 name: Bob"
3. 局限性
- 无法监听新增/删除的属性:需要手动调用
Vue.set或Vue.delete。 - 数组监听需要特殊处理:通过重写数组方法实现。
- 性能问题:递归遍历对象的所有属性,初始化时性能较差。
二、Vue 3 的 Proxy
1. 实现原理
- 代理对象:
Proxy直接代理整个对象(而非单个属性),可以监听所有操作(包括新增、删除属性,数组索引修改等)。 - 惰性监听:只有在访问属性时才会递归代理嵌套对象,性能更优。
2. 代码示例
// 简化版 Proxy 实现
function reactive(obj) {
return new Proxy(obj, {
get(target, key, receiver) {
console.log(`读取 ${key}`);
const res = Reflect.get(target, key, receiver);
// 如果是对象,递归代理
return typeof res === 'object' ? reactive(res) : res;
},
set(target, key, value, receiver) {
console.log(`设置 ${key}: ${value}`);
const success = Reflect.set(target, key, value, receiver);
if (success) {
// 触发视图更新(模拟)
updateView();
}
return success;
},
deleteProperty(target, key) {
console.log(`删除 ${key}`);
const success = Reflect.deleteProperty(target, key);
if (success) {
// 触发视图更新(模拟)
updateView();
}
return success;
},
});
}
// 测试
const data = reactive({ name: 'Alice' });
data.name = 'Bob'; // 输出 "设置 name: Bob"
delete data.name; // 输出 "删除 name"
data.age = 30; // 输出 "设置 age: 30"
3. 优势
- 全面监听:支持新增、删除属性,数组索引修改等。
- 性能优化:惰性代理,无需递归初始化。
- 更简洁的 API:无需特殊处理数组或动态属性。
三、对比总结
| 特性 | Vue 2 (Object.defineProperty) | Vue 3 (Proxy) |
|---|---|---|
| 监听范围 | 只能监听已存在的属性 | 支持新增、删除属性,数组索引修改等 |
| 数组监听 | 需要重写数组方法 | 直接监听数组索引变化 |
| 性能 | 初始化时递归遍历所有属性,性能较差 | 惰性代理,按需监听,性能更优 |
| 兼容性 | 支持 IE9+ | 不支持 IE(依赖 ES6 Proxy) |
| 代码复杂度 | 需要递归处理对象和数组 | 实现更简洁,逻辑更清晰 |
| 动态属性支持 | 需要 Vue.set/Vue.delete | 直接支持 |
四、为什么 Vue 3 选择 Proxy?
-
更强大的监听能力
Proxy可以监听所有操作(包括新增、删除属性,数组索引修改等),解决了Object.defineProperty的局限性。 -
性能优化
Proxy的惰性代理机制避免了递归初始化时的性能损耗。 -
简化代码
Proxy的 API 更简洁,无需特殊处理数组或动态属性。 -
面向未来
Proxy是 ES6 标准特性,随着浏览器对 ES6 的全面支持,Vue 3 放弃了对旧版本 IE 的兼容,拥抱现代浏览器。
五、示例场景对比
1. 动态新增属性
- Vue 2:需要
Vue.set。this.$set(this.data, 'newKey', 'value'); - Vue 3:直接赋值。
data.newKey = 'value'; // 自动触发更新
2. 数组索引修改
- Vue 2:无法监听直接通过索引修改数组。
this.list[0] = 'newValue'; // 不会触发视图更新 - Vue 3:直接监听。
list[0] = 'newValue'; // 触发视图更新
六、总结
- Vue 2 的
Object.defineProperty:通过递归劫持对象属性实现响应式,但存在无法监听动态属性、数组索引修改等局限性。 - Vue 3 的
Proxy:通过代理整个对象实现响应式,支持全面的操作监听,性能更优,代码更简洁。
593

被折叠的 条评论
为什么被折叠?



