Vue2/Vue3 响应式原理对比指南
1. 基本实现原理
1.1 Vue2 响应式实现 (Object.defineProperty)
// Vue2 响应式核心实现
function defineReactive(obj, key, val) {
// 递归处理嵌套对象
observe(val);
const dep = new Dep();
Object.defineProperty(obj, key, {
get() {
// 依赖收集
if (Dep.target) {
dep.depend();
}
return val;
},
set(newVal) {
if (val === newVal) return;
val = newVal;
// 触发更新
dep.notify();
}
});
}
// 遍历对象所有属性
function observe(obj) {
if (!obj || typeof obj !== 'object') return;
Object.keys(obj).forEach(key => {
defineReactive(obj, key, obj[key]);
});
}
1.2 Vue3 响应式实现 (Proxy)
// Vue3 响应式核心实现
function reactive(target) {
if (!isObject(target)) return target;
const handler = {
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 oldValue = target[key];
const result = Reflect.set(target, key, value, receiver);
// 触发更新
if (oldValue !== value) {
trigger(target, key);
}
return result;
},
deleteProperty(target, key) {
const hadKey = hasOwn(target, key);
const result = Reflect.deleteProperty(target, key);
if (hadKey && result) {
// 触发更新
trigger(target, key);
}
return result;
}
};
return new Proxy(target, handler);
}
2. 核心差异对比
2.1 实现机制
特性 | Vue2 (Object.defineProperty) | Vue3 (Proxy) |
---|---|---|
拦截方式 | 属性级别拦截 | 对象级别拦截 |
初始化时机 | 初始化时递归遍历所有属性 | 访问时才进行代理(懒代理) |
内存占用 | 需要为每个属性创建getter/setter | 只需要一个代理对象 |
属性删除/添加 | 需要通过 Vue.set/Vue.delete | 可以直接监听 |
2.2 性能对比
- 初始化性能:
// Vue2 - 需要递归遍历所有属性
function observe(obj) {
Object.keys(obj).forEach(key => {
defineReactive(obj, key, obj[key]);
// 递归处理嵌套对象
if (typeof obj[key] === 'object') {
observe(obj[key]);
}
});
}
// Vue3 - 访问时才代理
const proxy = new Proxy(target, {
get(target, key) {
// 只在访问时才进行代理
const value = Reflect.get(target, key);
return isObject(value) ? reactive(value) : value;
}
});
- 内存占用:
// Vue2 - 每个属性都需要定义getter/setter
const obj = { a: 1, b: 2, c: 3 };
// 需要创建3个getter/setter
// Vue3 - 只需要一个代理对象
const proxy = new Proxy(obj, handler);
// 只需要创建一个Proxy实例
2.3 功能特性对比
- 数组操作:
// Vue2 - 需要重写数组方法
const arrayMethods = ['push', 'pop', 'shift', 'unshift', 'splice'];
arrayMethods.forEach(method => {
// 重写数组方法以触发更新
});
// Vue3 - 直接支持数组操作
const arr = reactive([1, 2, 3]);
arr.push(4); // 自动触发更新
arr[1] = 5; // 自动触发更新
- 新增属性:
// Vue2
const obj = { a: 1 };
// 新增属性需要使用 Vue.set
Vue.set(obj, 'b', 2);
// Vue3
const obj = reactive({ a: 1 });
// 直接添加即可
obj.b = 2; // 自动触发更新
3. 优缺点分析
3.1 Vue2 (Object.defineProperty)
优点:
- 兼容性好,支持 IE8+
- 代码实现相对简单
缺点:
- 需要递归遍历对象所有属性
- 无法监听数组索引和长度变化
- 无法监听对象属性的添加和删除
- 需要额外的 API (Vue.set/Vue.delete)
3.2 Vue3 (Proxy)
优点:
- 性能更好(懒代理)
- 功能更强大(可以监听更多操作)
- 代码更简洁(不需要递归)
- 可以监听动态属性
缺点:
- 兼容性较差(不支持 IE11)
- 无法 polyfill
4. 为什么 Proxy 更高效?
- 初始化效率:
- Object.defineProperty 需要递归遍历对象的所有属性
- Proxy 采用懒代理,只在访问时才创建代理对象
- 内存占用:
- Object.defineProperty 需要为每个属性创建 getter/setter
- Proxy 只需要创建一个代理对象
- 操作拦截:
- Object.defineProperty 只能拦截属性的读写
- Proxy 可以拦截多达 13 种操作
- 数组处理:
- Object.defineProperty 需要重写数组方法
- Proxy 可以直接监听数组操作
5. 实际应用建议
- Vue2 项目:
- 避免深层嵌套数据结构
- 使用扁平化的数据结构
- 合理使用 Vue.set/Vue.delete
- Vue3 项目:
- 可以更自由地使用嵌套数据
- 直接操作数组和对象
- 利用 Proxy 的特性优化性能
6. 总结
Vue3 的响应式系统相比 Vue2 有显著改进:
- 性能更好
- 功能更强大
- 代码更简洁
- 开发体验更好
选择建议:
- 新项目建议使用 Vue3
- 需要兼容 IE11 的项目使用 Vue2
- 大型项目推荐 Vue3(性能优势明显)