Vue3 的组合式 API 和响应式系统是其核心亮点,通过 Proxy 实现的响应式机制相比 Vue2 的 Object.defineProperty 有了质的提升。本文将深入解析 Vue3 响应式系统的实现原理,结合代码示例帮助理解其工作机制。
一、Vue3 响应式系统的核心组件
1. 三大核心模块
- reactive:创建响应式对象
- effect:副作用函数,依赖收集的载体
- track:依赖收集
- trigger:触发更新
2. 响应式系统的工作流程
- 通过 reactive 创建响应式对象
- effect 注册副作用函数并执行
- 副作用函数访问响应式对象属性时触发 track 收集依赖
- 响应式对象属性变更时触发 trigger 通知更新
二、响应式系统的基本实现
1. 基础响应式对象实现
// 存储依赖关系的 WeakMap
const targetMap = new WeakMap();
// 收集依赖
function track(target, key) {
// 获取当前激活的 effect
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()));
}
// 将当前 effect 添加到依赖集合中
dep.add(activeEffect);
// 反向收集,便于清理
activeEffect.deps.push(dep);
}
// 触发更新
function trigger(target, key) {
const depsMap = targetMap.get(target);
if (!depsMap) return;
// 获取属性的依赖集合
const dep = depsMap.get(key);
if (!dep) return;
// 执行所有依赖的 effect
dep.forEach(effect => effect());
}
// 创建响应式对象
function reactive(target) {
return new Proxy(target, {
get(target, key, receiver) {
const result = Reflect.get(target, key, receiver);
// 收集依赖
track(target, key);
return 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;
}
});
}
2. effect 函数实现
// 当前激活的 effect
let activeEffect = null;
// 创建副作用函数
function effect(fn) {
const effectFn = () => {
try {
// 设置当前激活的 effect
activeEffect = effectFn;
// 清理之前的依赖
cleanup(effectFn);
// 执行副作用函数
return fn();
} finally {
// 重置当前激活的 effect
activeEffect = null;
}
};
// 存储依赖集合
effectFn.deps = [];
// 立即执行一次
effectFn();
return effectFn;
}
// 清理依赖
function cleanup(effectFn) {
for (let i = 0; i < effectFn.deps.length; i++) {
const dep = effectFn.deps[i];
dep.delete(effectFn);
}
effectFn.deps.length = 0;
}
三、使用示例
1. 简单响应式示例
// 创建响应式对象
const state = reactive({
count: 0,
message: 'Hello Vue3'
});
// 创建副作用函数
effect(() => {
console.log(`count is: ${state.count}`);
});
// 修改属性,触发更新
state.count = 1; // 输出: count is: 1
state.count = 2; // 输出: count is: 2
2. 嵌套响应式对象
const state = reactive({
user: {
name: 'John',
age: 30
},
list: [1, 2, 3]
});
effect(() => {
console.log(`User: ${state.user.name}, Age: ${state.user.age}`);
});
// 修改嵌套属性
state.user.age = 31; // 输出: User: John, Age: 31
四、Vue3 响应式系统的优化
1. 与 Vue2 的对比优势
- 深度响应式:默认递归监听所有属性
- 支持新增/删除属性:Proxy 可以拦截 delete 操作
- 支持 Map、Set 等数据结构:Object.defineProperty 仅支持对象属性
- 更好的性能:不需要预先遍历所有属性
2. 性能优化
// 浅响应式,只监听第一层属性
function shallowReactive(target) {
return new Proxy(target, {
get(target, key, receiver) {
const result = Reflect.get(target, key, receiver);
track(target, key);
return 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;
}
});
}
// 只读响应式
function readonly(target) {
return new Proxy(target, {
get(target, key, receiver) {
const result = Reflect.get(target, key, receiver);
return result;
},
set(target, key, value, receiver) {
console.warn(`Cannot set readonly property: ${key}`);
return true;
}
});
}
五、Vue3 响应式系统的高级特性
1. computed 实现
function computed(getter) {
let value;
let dirty = true;
const effectFn = effect(() => {
// 缓存计算结果
value = getter();
dirty = false;
});
return {
get value() {
if (dirty) {
effectFn();
}
// 收集依赖
track(this, 'value');
return value;
}
};
}
2. watch 实现
function watch(source, cb) {
let getter;
// 处理不同类型的 source
if (typeof source === 'function') {
getter = source;
} else {
getter = () => traverse(source);
}
let oldValue, newValue;
// 创建副作用函数
const effectFn = effect(() => {
newValue = getter();
if (oldValue !== undefined) {
cb(newValue, oldValue);
}
oldValue = newValue;
});
}
// 递归遍历对象,触发所有属性的依赖收集
function traverse(value, seen = new Set()) {
if (typeof value !== 'object' || value === null || seen.has(value)) {
return;
}
seen.add(value);
// 遍历对象的所有属性
for (const key in value) {
traverse(value[key], seen);
}
return value;
}
六、总结
Vue3 的响应式系统通过 Proxy 实现,相比 Vue2 有了显著的性能提升和功能增强。其核心原理是通过 Proxy 拦截对象的属性访问和修改,结合 effect 函数实现依赖收集和自动更新。
在实际应用中,理解响应式原理有助于编写更高效的组件,避免不必要的更新。同时,Vue3 提供的 shallowReactive、readonly、computed 和 watch 等高级特性,进一步增强了响应式系统的灵活性和实用性。
通过深入理解 Vue3 响应式原理,可以更好地利用其特性构建高性能、可维护的前端应用。