前言
最近在看vue3 的源码时,遇到了两个概念 依赖收集和派发更新,首先顶级两个全局变量,用来存放和定位追踪的依赖,及track和trigger使用的仓库
let targetMap = new WeakMap();
let activeEffect;
依赖收集
作用
track(obj, 'get', 'x');
track 会去找 obj.x 是否被追踪,如果没找到就将obj.x放入targetMap(完成追踪任务),将 obj.x 作为 map 的 key 将 activeEffect 作为 map 的 value。
抛开取值异常处理之类的,track 只做了一件事,将activeEffect塞入targetMap;
从这,我们可了解targetMap的格式如下
targetMap -> WeakMap()
| 名称 | key | value |
|---|---|---|
| targetMap | 属性名称 | Map() |
| Map | 属性名称 | Set() |
track源码
function track(target, key) {
// 首先找 obj 是否有被追踪
let depsMap = targetMap.get(target);
if (!depsMap) {
// 如果没有被追踪,那么添加一个
targetMap.set(target, (depsMap = new Map()));
}
// 然后寻找 obj.x 是否被追踪
let dep = depsMap.get(key);
if (!dep) {
// 如果没有被追踪,那么添加一个
depsMap.set(key, (dep = new Set()));
}
// 如果没有添加 activeEffect 那么添加一个
if (!dep.has(activeEffect)) {
dep.add(activeEffect);
activeEffect.deps.push(dep);
if (activeEffect.options.onTrack) {
activeEffect.options.onTrack({
effect: activeEffect,
target,
type,
key
});
}
}
}
派发更新
作用
然后就是写一个 trigger,还记得trigger在vue是如何调用的吗?
trigger(obj, 'set', 'x')
trigger 只会去 targetMap 中寻找obj.x的追踪任务,如果找到了就去重,然后执行任务。
也就是说:抛开取值异常相关,trigger 也只做了一件事:从 targetMap 取值然后调用该函数值。
源码中此时会给该响应式数据根据类型遍历添加相应的方法
源码
function trigger(target, key) {
// 寻找追踪项
const depsMap = targetMap.get(target);
// 没找到就什么都不干
if (!depsMap) return;
// 去重
const effects = new Set()
depsMap.get(key).forEach(e => effects.add(e))
// 执行
effects.forEach(e => e())
}