Vue3响应式原理解析
一、什么是响应式?
想象一个 Excel 表格:当单元格 A1 的值变化时,依赖 A1 的公式(如 SUM(A1, B1))会自动重新计算。Vue3 的响应式系统正是实现了这种“自动更新”的能力——当数据变化时,依赖它的视图或计算属性会自动更新。
二、Vue3 响应式的核心实现
1. 底层工具:Proxy 与 Reflect
Vue3 使用 Proxy 代替 Vue2 的 Object.defineProperty
,因为它能拦截对象的所有操作(如属性读取、新增、删除等),而无需预先定义属性149。
const obj = { a: 1 };
const proxy = new Proxy(obj, {
get(target, key) {
console.log('读取属性:', key);
return Reflect.get(target, key); // 使用 Reflect 保证 this 指向正确
},
set(target, key, value) {
console.log('设置属性:', key, value);
return Reflect.set(target, key, value);
}
});
2. 响应式对象:reactive()
reactive()
函数将普通对象转为响应式对象,内部通过 Proxy 实现:
function reactive(target) {
return new Proxy(target, {
get(target, key) {
track(target, key); // 收集依赖
const res = Reflect.get(target, key);
// 递归处理嵌套对象
return isObject(res) ? reactive(res) : res;
},
set(target, key, value) {
Reflect.set(target, key, value);
trigger(target, key); // 触发更新
return true;
}
});
}
关键点:
-
深层响应:嵌套对象会被递归代理1。
-
避免重复代理:通过
WeakMap
缓存已代理对象10。
3. 副作用函数:effect()
副作用函数(如渲染函数)会在依赖的响应式数据变化时重新执行:
let activeEffect;
function effect(fn) {
const _effect = () => {
activeEffect = _effect; // 标记当前正在执行的副作用函数
fn();
activeEffect = null;
};
_effect(); // 立即执行一次以收集依赖
}
// 示例
effect(() => {
console.log('数据变化了:', state.a);
});
4. 依赖收集与触发更新
(1) 数据结构
-
targetMap
:WeakMap
,键为响应式对象,值为depsMap
。 -
depsMap
:Map
,键为对象的属性名,值为dep
(依赖集合)。 -
dep
:Set
,存储所有依赖该属性的副作用函数210。
(2) track():收集依赖
在读取属性时,将当前副作用函数加入依赖集合:
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);
}
(3) trigger():触发更新
在设置属性时,执行所有依赖该属性的副作用函数:
function trigger(target, key) {
const depsMap = targetMap.get(target);
if (!depsMap) return;
const dep = depsMap.get(key);
dep && dep.forEach(effect => effect());
}
三、Vue3 对比 Vue2 的优势49
特性 | Vue2 (Object.defineProperty) | Vue3 (Proxy) |
---|---|---|
检测新增属性 | 需手动调用 Vue.set() | 自动支持 |
数组索引修改 | 需重写数组方法 | 直接通过下标修改即可 |
嵌套对象处理 | 需递归初始化 | 按需代理(惰性处理) |
性能 | 初始化时递归遍历所有属性 | 动态代理,按需收集依赖 |
四、实战示例
1. 基础响应式
const state = reactive({ count: 0 });
effect(() => {
console.log('count 值变为:', state.count);
});
state.count++; // 触发控制台输出
2. 嵌套对象
const obj = reactive({
foo: { bar: 1 }
});
effect(() => {
console.log(obj.foo.bar); // 深层属性仍可响应
});
obj.foo.bar = 2; // 触发更新
五、总结
Vue3 的响应式系统通过 Proxy 拦截对象操作,结合 依赖收集(track) 与 触发更新(trigger) 实现了高效的数据绑定。相比 Vue2,它解决了动态属性、数组操作等痛点,且性能更优。理解其核心原理有助于更好地使用 Vue3 开发复杂应用。
想深入学习可参考:Vue3 响应式官方文档