以下是使用 ES6 class 语法实现的 Vue2 (Object.defineProperty) 和 Vue3 (Proxy) 响应式系统核心逻辑的示例代码:
一、Vue2 风格:基于 Object.defineProperty
class Vue2Reactive {
constructor(data) {
this.data = data;
this.walk(data);
}
// 递归遍历对象属性
walk(obj) {
if (typeof obj !== 'object' || obj === null) return;
Object.keys(obj).forEach(key => {
this.defineReactive(obj, key, obj[key]);
this.walk(obj[key]); // 递归处理嵌套对象
});
// 处理数组监听
if (Array.isArray(obj)) {
this.overrideArrayMethods(obj);
}
}
// 定义响应式属性
defineReactive(obj, key, value) {
const dep = new Dep(); // 模拟依赖收集器
Object.defineProperty(obj, key, {
get: () => {
dep.depend(); // 收集依赖
return value;
},
set: newVal => {
if (value === newVal) return;
value = newVal;
this.walk(newVal); // 对新值进行响应式处理
dep.notify(); // 触发更新
}
});
}
// 重写数组方法
overrideArrayMethods(arr) {
const arrayProto = Array.prototype;
const methods = ['push', 'pop', 'shift', 'unshift'];
const arrayMethods = Object.create(arrayProto);
methods.forEach(method => {
const original = arrayProto[method];
Object.defineProperty(arrayMethods, method, {
value: (...args) => {
const result = original.apply(arr, args);
console.log(`数组被修改: ${method}`);
this.walk(arr); // 重新遍历数组元素
return result;
}
});
});
arr.__proto__ = arrayMethods;
}
}
// 使用示例
const data = {
count: 0,
list: [1, 2, 3],
nested: { a: 1 }
};
const reactiveData = new Vue2Reactive(data);
data.count = 5; // 触发 setter
data.list.push(4); // 输出 "数组被修改: push"
data.newProp = 'test'; // 不会被监听(需使用 Vue.set)
二、Vue3 风格:基于 Proxy
class Vue3Reactive {
constructor(data) {
return this.createProxy(data);
}
createProxy(target) {
const dep = new Dep(); // 模拟依赖收集器
const handler = {
get: (target, key, receiver) => {
dep.depend(); // 收集依赖
const value = Reflect.get(target, key, receiver);
// 递归代理嵌套对象
return typeof value === 'object' && value !== null
? this.createProxy(value)
: value;
},
set: (target, key, value, receiver) => {
if (target[key] === value) return true;
const result = Reflect.set(target, key, value, receiver);
dep.notify(); // 触发更新
return result;
},
deleteProperty: (target, key) => {
const result = Reflect.deleteProperty(target, key);
dep.notify();
return result;
}
};
return new Proxy(target, handler);
}
}
// 使用示例
const data = {
count: 0,
list: [1, 2, 3],
nested: { a: 1 }
};
const reactiveData = new Vue3Reactive(data);
reactiveData.count = 5; // 触发更新
reactiveData.list.push(4); // 自动监听数组操作
reactiveData.newProp = 'test'; // 自动监听新增属性
delete reactiveData.nested.a; // 触发删除监听
对比
四、实现要点说明
- 依赖收集
两个类都使用简化的 Dep 类(未完整实现)模拟依赖收集,实际Vue中使用的是更复杂的依赖追踪系统。 - 递归处理
- Vue2 在初始化时递归遍历所有属性
- Vue3 仅在访问属性时动态创建嵌套代理
- 性能优化
Proxy 方案避免了初始化时的全量递归,对大型对象性能更优。
五、扩展建议
- 添加 Dep 类的完整实现:
class Dep {
constructor() {
this.subscribers = new Set();
}
depend() {
if (activeEffect) {
this.subscribers.add(activeEffect);
}
}
notify() {
this.subscribers.forEach(effect => effect());
}
}
let activeEffect = null;
function watchEffect(effect) {
activeEffect = effect;
effect();
activeEffect = null;
}
2.实际使用:
const data = new Vue3Reactive({ count: 0 });
watchEffect(() => {
console.log(`Count changed: ${data.count}`);
});
data.count++; // 输出 "Count changed: 1"