返回博客
技术 2025年2月3日 3 分钟阅读 · 653 字
Vue 3 Composition API 响应式原理:从 Proxy 到依赖收集
深入 Vue 3 响应式系统的底层实现:Proxy 代理、track 依赖收集、trigger 派发更新的完整流程。
#Vue.js
#JavaScript
#响应式
#源码分析
本文由 AI 辅助生成,经人工审核发布
从 Object.defineProperty 到 Proxy
Vue 2 使用 Object.defineProperty 实现响应式,存在两个致命缺陷:
- 无法检测属性新增/删除:需要
Vue.set()/Vue.delete() - 无法监听数组索引和 length 变化:需要重写数组原型方法
Vue 3 用 Proxy 彻底解决了这些问题:
// Vue 3 响应式核心(简化版)
function reactive(target) {
const handler = {
get(target, key, receiver) {
track(target, key); // 依赖收集
return Reflect.get(target, key, receiver);
},
set(target, key, value, receiver) {
const result = Reflect.set(target, key, value, receiver);
trigger(target, key); // 派发更新
return result;
}
};
return new Proxy(target, handler);
}
Proxy 可以拦截 13 种操作(包括 has、deleteProperty、ownKeys 等),比 Object.defineProperty 强大得多。
依赖收集:track 函数
track 的作用是建立「属性 → 依赖该属性的 effect」的映射关系:
let activeEffect = null;
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 dep = depsMap.get(key);
if (!dep) {
dep = new Set();
depsMap.set(key, dep);
}
dep.add(activeEffect);
}
数据结构是三层 Map:
targetMap (WeakMap)
└─ target → depsMap (Map)
└─ key → dep (Set<effect>)
使用 WeakMap 是因为当 target 对象被销毁时,对应的依赖关系会自动被垃圾回收。
派发更新:trigger 函数
当响应式数据被修改时,trigger 找到所有依赖该属性的 effect 并执行:
function trigger(target, key) {
const depsMap = targetMap.get(target);
if (!depsMap) return;
const dep = depsMap.get(key);
if (dep) {
dep.forEach(effect => effect());
}
}
effect 与 computed
effect 是响应式系统的基础原语。computed 和 watch 都建立在 effect 之上:
// computed 的简化实现
function computed(getter) {
let value;
let dirty = true;
const runner = effect(() => {
value = getter();
dirty = false;
});
return {
get value() {
if (dirty) {
runner(); // 重新计算
}
return value;
}
};
}
computed 本质上是一个带缓存的 effect。当依赖的响应式数据变化时,trigger 会将 dirty 设为 true,下次访问 .value 时才重新计算。
ref 的实现
ref 是对原始值的包装。因为 Proxy 无法代理原始值(如 number、string),所以需要用对象包装:
function ref(value) {
return {
get value() {
track(this, 'value');
return value;
},
set value(newVal) {
value = newVal;
trigger(this, 'value');
}
};
}
这就是为什么访问 ref 需要 .value。
性能优化:惰性响应式
Vue 3.2+ 引入了 shallowRef 和 shallowReactive,只代理第一层:
const state = shallowReactive({ user: { name: 'Li' } });
// state.user 的修改不会触发响应式
// 需要手动触发:triggerRef(state, 'user')
在处理大型嵌套对象时,这可以显著减少 Proxy 代理的性能开销。
总结
Vue 3 的响应式系统是一个精妙的工程设计:
- Proxy 提供了完整的拦截能力
- WeakMap + Map + Set 三层结构实现了高效的依赖追踪
- effect 作为基础原语,computed 和 watch 都建立其上
- 惰性求值和 shallow 模式提供了细粒度的性能控制