一、Vue 2 响应式实现详解
1. 核心代码实现
// 依赖收集器(观察者模式)
class Dep {constructor() {this.subscribers = new Set();}depend() {if (activeEffect) {this.subscribers.add(activeEffect);}}notify() {this.subscribers.forEach(effect => effect());}
}let activeEffect = null;// 响应式转换核心
function defineReactive(obj, key, val) {const dep = new Dep();Object.defineProperty(obj, key, {get() {dep.depend(); // 收集依赖return val;},set(newVal) {if (newVal !== val) {val = newVal;dep.notify(); // 触发更新}}});
}// 递归处理对象
function observe(obj) {Object.keys(obj).forEach(key => {let value = obj[key];if (typeof value === 'object' && value !== null) {observe(value); // 递归处理嵌套对象}defineReactive(obj, key, value);});
}// 数组方法重写
const arrayProto = Array.prototype;
const arrayMethods = Object.create(arrayProto);
['push', 'pop', 'shift', 'unshift'].forEach(method => {const original = arrayProto[method];arrayMethods[method] = function(...args) {const result = original.apply(this, args);this.__ob__.dep.notify(); // 手动触发更新return result;};
});
2. 使用步骤
const data = { count: 0, list: [1,2,3] };
observe(data);// 定义响应式副作用
function watchEffect(fn) {activeEffect = fn;fn();activeEffect = null;
}watchEffect(() => {console.log('Count changed:', data.count);
});data.count = 5; // 触发日志输出
data.list.push(4); // 触发更新
3. 关键问题示例
// 动态属性问题
data.newProp = 'test'; // ❌ 不会触发更新
Vue.set(data, 'newProp', 'test'); // ✅ 正确方式// 数组索引修改问题
data.list[0] = 99; // ❌ 不会触发
data.list.splice(0, 1, 99); // ✅ 正确方式
二、Vue 3 响应式实现详解
1. 核心代码实现
const targetMap = new WeakMap(); // 存储对象-键-依赖关系function track(target, key) {if (!activeEffect) return;let depsMap = targetMap.get(target);if (!depsMap) {targetMap.set(target, (depsMap = new Map()));}let dep = depsMap.get(key);if (!dep) {depsMap.set(key, (dep = new Set()));}dep.add(activeEffect);
}function trigger(target, key) {const depsMap = targetMap.get(target);if (!depsMap) return;const effects = depsMap.get(key);effects && effects.forEach(effect => effect());
}const reactiveMap = new WeakMap();function reactive(obj) {if (reactiveMap.has(obj)) return reactiveMap.get(obj);const proxy = new Proxy(obj, {get(target, key, receiver) {track(target, key);const res = Reflect.get(target, key, receiver);if (typeof res === 'object' && res !== null) {return reactive(res); // 惰性递归代理}return res;},set(target, key, value, receiver) {Reflect.set(target, key, value, receiver);trigger(target, key);return true;},// 处理 delete 操作deleteProperty(target, key) {const hasKey = Object.prototype.hasOwnProperty.call(target, key);const result = Reflect.deleteProperty(target, key);if (hasKey && result) {trigger(target, key);}return result;}});reactiveMap.set(obj, proxy);return proxy;
}// 处理基本类型
function ref(value) {return {get value() {track(this, 'value');return value;},set value(newVal) {value = newVal;trigger(this, 'value');}};
}
2. 使用步骤
const state = reactive({ count: 0, items: ['apple'] });
const num = ref(10);// 响应式副作用
function effect(fn) {activeEffect = fn;fn();activeEffect = null;
}effect(() => {console.log('State changed:', state.count, num.value);
});state.count++; // ✅ 触发更新
state.items.push('banana'); // ✅ 自动触发
num.value = 20; // ✅ 触发更新
三、技术对比全景图
特性 | Vue 2 (Object.defineProperty) | Vue 3 (Proxy) |
---|---|---|
动态属性检测 | 需手动调用 Vue.set | 自动检测 |
数组处理 | 需重写 7 个方法 | 原生支持所有操作 |
性能初始化 | O(n) 立即递归 | O(1) 惰性代理 |
嵌套对象处理 | 初始化时完全递归 | 按需代理 |
数据类型支持 | 对象/数组 | 支持 Map/Set/WeakMap 等 |
兼容性 | IE9+ | 不支持 IE11 及以下 |
内存占用 | 每个属性创建 Dep 实例 | 整个对象共享依赖映射 |
四、关键演进技术点
1. 依赖收集机制升级
- Vue 2:每个属性对应一个 Dep 实例
- Vue 3:通过
WeakMap
建立三级映射关系:targetMap: WeakMap<Target, depsMap> depsMap: Map<Key, dep> dep: Set<Effect>
2. 性能优化策略
- 惰性代理:只有访问到的属性才会被代理
- 缓存机制:
reactiveMap
避免重复代理同一对象 - 批量更新:通过调度器合并多次数据变更
3. 新数据结构支持
// 支持 Map 的响应式
const mapState = reactive(new Map());
mapState.set('key', 'value'); // ✅ 自动触发更新// 支持 Set 的操作
const setState = reactive(new Set());
setState.add(123); // ✅ 触发更新
五、最佳实践指南
Vue 2 项目注意事项
- 动态添加属性必须使用
Vue.set
- 数组操作优先使用变异方法
- 复杂数据结构建议提前初始化完整结构
Vue 3 开发技巧
// 解构保持响应式
const state = reactive({ x: 1, y: 2 });
const { x, y } = toRefs(state); // 使用 toRefs// 组合式 API 示例
import { reactive, watchEffect } from 'vue';export default {setup() {const state = reactive({ count: 0 });watchEffect(() => {console.log('Current count:', state.count);});return { state };}
}
六、总结与展望
核心演进价值
- 开发体验:减少心智负担,代码更符合直觉
- 性能突破:大型应用响应式处理效率提升 2-5 倍
- 扩展能力:为未来响应式数据库等高级特性铺路
通过深入理解 Vue 响应式系统的演进,开发者不仅能写出更高效的代码,更能把握前端框架设计的核心思想。这种从「属性劫持」到「代理拦截」的转变,体现了前端技术追求更优雅、更高效的永恒主题。