Vue3响应式原理与实现详解
字数 947 2025-11-10 02:41:35
Vue3响应式原理与实现详解
1. 响应式系统的核心目标
Vue3的响应式系统通过Proxy和Reflect实现,其核心目标是:当数据变化时,自动触发依赖该数据的副作用函数(如视图更新、计算属性重新计算等)。
2. 核心概念:副作用函数(Effect)
副作用函数指执行时会直接或间接读取响应式数据的函数。例如:
const data = { count: 1 };
function effect() {
document.body.innerText = data.count; // 读取data.count
}
理想情况下,当data.count变化时,应重新执行effect函数。
3. 依赖收集与触发更新
3.1 依赖收集的原理
-
存储结构:使用
WeakMap(target → Map(key → Set(effect)))三层结构存储依赖关系。WeakMap的键是响应式对象(target),值是一个Map。Map的键是对象的属性(key),值是一个Set,存储所有依赖该属性的副作用函数。
-
触发时机:
- 读取数据时(get):将当前执行的副作用函数存入对应属性的依赖集合中。
- 修改数据时(set):从依赖集合中取出所有副作用函数并执行。
3.2 实现步骤
步骤1:创建临时变量存储当前副作用函数
let activeEffect = null; // 当前正在执行的副作用函数
步骤2:定义依赖收集函数(track)
const targetMap = new WeakMap(); // 全局依赖存储
function track(target, key) {
if (!activeEffect) return;
let depsMap = targetMap.get(target);
if (!depsMap) {
depsMap = new Map();
targetMap.set(target, depsMap);
}
let deps = depsMap.get(key);
if (!deps) {
deps = new Set();
depsMap.set(key, deps);
}
deps.add(activeEffect); // 将当前副作用函数加入依赖集合
}
步骤3:定义触发更新函数(trigger)
function trigger(target, key) {
const depsMap = targetMap.get(target);
if (!depsMap) return;
const deps = depsMap.get(key);
if (deps) {
deps.forEach(effect => effect()); // 重新执行所有依赖函数
}
}
4. 响应式对象的实现
通过Proxy代理对象的get和set操作:
function reactive(target) {
return new Proxy(target, {
get(target, key, receiver) {
track(target, key); // 读取时收集依赖
return Reflect.get(target, key, receiver);
},
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;
}
});
}
5. 副作用函数的注册
定义effect函数,用于注册副作用函数并立即执行一次:
function effect(fn) {
activeEffect = fn; // 设置当前活跃的副作用函数
fn(); // 执行函数,触发get操作以收集依赖
activeEffect = null; // 重置
}
6. 完整示例测试
const data = reactive({ count: 1 });
effect(() => {
console.log("count变化了:", data.count);
});
data.count = 2; // 输出:"count变化了: 2"
7. 进阶优化:分支切换与cleanup
若副作用函数中存在条件分支(如data.flag ? data.a : data.b),当flag变化时需清理不再依赖的属性。Vue3通过给副作用函数绑定deps数组,并在每次执行前清理旧依赖来实现。
8. 总结
Vue3响应式系统的核心流程:
- 依赖收集:通过Proxy的get拦截,将副作用函数存入对应属性的依赖集合。
- 触发更新:通过Proxy的set拦截,执行依赖集合中的所有副作用函数。
- 副作用管理:通过
effect函数注册副作用,并处理分支切换的依赖清理。
与Vue2的Object.defineProperty相比,Proxy能直接代理对象(而非属性),无需递归初始化,且支持动态新增属性的响应式。