響應式依賴和更新是Vue 3.0中最重要的機制,其核心代碼如下,本文將結合代碼對這個設計機制作出一些解釋。
// 全局依賴存儲:WeakMap<target, Map<key, Set<effect>>>
const targetMap = new WeakMap();// 當前活動的副作用函數(如組件的渲染函數)
let activeEffect = null;// 響應式入口函數
function reactive(target) {return createReactiveObject(target, mutableHandlers);
}// 創建響應式代理對象
function createReactiveObject(target, handlers) {if (typeof target !== 'object' || target === null) {return target; // 非對象直接返回}return new Proxy(target, handlers);
}// Proxy 攔截器配置
const mutableHandlers = {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) {const oldValue = target[key];const result = Reflect.set(target, key, value, receiver);if (oldValue !== value) {trigger(target, key); // 觸發更新}return result;},// 其他攔截器(deleteProperty/has/ownKeys 等省略)
};// 依賴收集:建立 target.key → effect 的映射
function track(target, key) {if (!activeEffect) return; // 無活動 effect 則退出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()));}if (!dep.has(activeEffect)) {dep.add(activeEffect); // 添加 effect 到依賴集合activeEffect.deps.push(dep); // 反向關聯(用于清理)}
}// 觸發更新:執行 target.key 的所有依賴 effect
function trigger(target, key) {const depsMap = targetMap.get(target);if (!depsMap) return;const effects = new Set();const dep = depsMap.get(key);if (dep) {dep.forEach(effect => effects.add(effect));}effects.forEach(effect => effect()); // 重新執行副作用函數
}// 副作用函數封裝(示例)
function effect(fn) {const effectFn = () => {activeEffect = effectFn;fn();activeEffect = null;};effectFn.deps = []; // 存儲所有關聯的 dep 集合effectFn();
}
1. 核心數據結構:targetMap
類型:WeakMap<Object, Map<string, Set<Effect>>>
作用:全局存儲所有響應式對象的依賴關系。
鍵:被代理的原始對象Object。
值:一個?Map,其鍵是對象的屬性名,值是一個?Set,存儲了與該屬性相關的所有副作用函數,如組件的渲染函數。
2.?track 函數:依賴收集
觸發時機:當訪問響應式對象的某個屬性時,即觸發get操作。
核心流程:
1. 獲取當前活動的副作用函數:activeEffect?指向當前正在執行的副作用函數,例如組件渲染函數或?watch?回調;
2. 初始化依賴關系:從?targetMap?中獲取目標對象?target?對應的?depsMap,若不存在,則新建一個?Map。從?depsMap?中獲取屬性?key?對應的依賴集合?dep,若不存在,則新建一個?Set;
3. 建立雙向關聯:將?activeEffect?添加到?dep 中,表示該屬性依賴此副作用函數。將?dep?添加到?activeEffect.deps中,用于后續清理無效依賴,避免內存泄漏;
const obj = reactive({ count: 0 });
effect(() => console.log(obj.count));
// 執行 effect 時,`activeEffect` 指向該函數
// 訪問 obj.count 時觸發 track,將 effect 函數添加到 count 的依賴集合中。
3. trigger 函數:觸發更新
觸發時機:當修改響應式對象的屬性時 ,即觸發set 操作。
核心流程:
1. 獲取目標對象的依賴集合:從?targetMap?中獲取?target?對應的?depsMap;
2. 收集所有相關副作用函數:從?depsMap?中獲取屬性?key?對應的?dep,即該屬性的所有依賴函數。將dep中的所有?effect?添加到一個臨時集合?effects?中;
3. 執行副作用函數:遍歷?effects,依次執行每個?effect,觸發視圖更新或回調邏輯;?
obj.count++; // 修改 count 時觸發 trigger,執行所有依賴 count 的 effect。
4. 關鍵設計細節
1. WeakMap?的作用:鍵是弱引用,當目標對象?target?不再被引用時,targetMap?中對應的條目會被自動垃圾回收,避免內存泄漏;?
2. activeEffect?的管理:通過?effect?函數或組件渲染過程,將當前運行的副作用函數賦值給?activeEffect。執行完畢后,activeEffect?會被重置為?null,確保依賴收集的準確性;?
3. 雙向依賴關系:effect.deps?存儲了所有與該副作用函數相關的依賴集合dep。當副作用函數被銷毀時,可以通過遍歷?effect.deps?從所有?dep?中移除該函數,避免無效更新;?
4. 性能優化:使用?Set?存儲依賴函數,避免重復添加。觸發更新時,通過臨時集合?effects?確保每個?effect?只執行一次,避免循環觸發;?
5. 總結
依賴收集(Track):通過?track?函數,在屬性被訪問時記錄當前活動的副作用函數,建立屬性與副作用函數的關聯。
觸發更新(Trigger):通過?trigger?函數,在屬性被修改時找到所有相關副作用函數并執行,實現數據變化到視圖更新的響應式機制。
全局依賴關系:targetMap?作為核心數據結構,通過?WeakMap?和嵌套的?Map/Set,高效管理對象、屬性和副作用函數之間的映射關系。