一、介绍
之前看ref源码,发现其是通过track进行依赖收集,通过trigger进行更新通知的。这两个函数都定义在effect.ts文件中。
二、源码分析
2.1: track
// packages\reactivity\src\effect.ts
// 当前活跃的副作用,类似于vue2中的watcher
let activeEffect: ReactiveEffect | undefined
// 用于储存 原始对象和activeEffect的依赖关系
// 是一个2维的map
// 第一层的map: key是原始对象,value是一个map
// 第二层的map: key是原始对象的属性名称,value是该属性的依赖集合
const targetMap = new WeakMap<any, KeyToDepMap>()
// 第一个参数:原始对象
// 第二个参数:类型
// 第三个参数:原始对象的属性名称,即key
export function track(target: object, type: TrackOpTypes, key: unknown) {
// 判断是否应该进行track
if (!shouldTrack || activeEffect === undefined) {
return
}
// 当前targetMap中有没有存储该原始对象的依赖关系
let depsMap = targetMap.get(target)
if (!depsMap) {
// 如果没有存储,就把该原始对象作为key,值是一个新建的空map
targetMap.set(target, (depsMap = new Map()))
}
// 有没有key对应的依赖集合
let dep = depsMap.get(key)
if (!dep) {
// 如果没有依赖集合,就新建一个空的set存储依赖
depsMap.set(key, (dep = new Set()))
}
// 判断依赖集合中有没有当前应该收集的activeEffect
if (!dep.has(activeEffect)) {
// 如果当前activeEffect没有被收集,则应该收集
dep.add(activeEffect)
// activeEffect的依赖中也进行同步收集,相当于两边相互收集了对方
activeEffect.deps.push(dep)
}
}
总结:track方法主要是维护targetMap,targetMap中维护了原始对象各属性应该收集的activeEffect。
2.2 trigger
export function trigger(
target: object,
type: TriggerOpTypes,
key?: unknown,
newValue?: unknown,
oldValue?: unknown,
oldTarget?: Map<unknown, unknown> | Set<unknown>
) {
const depsMap = targetMap.get(target)
// 该属性从未被track,不需要触发更新通知,直接返回
if (!depsMap) {
// never been tracked
return
}
// 定义一个set,存储需要通知的activeEffect
const effects = new Set<ReactiveEffect>()
// 定义add方法
const add = (effectsToAdd: Set<ReactiveEffect> | undefined) => {
if (effectsToAdd) {
effectsToAdd.forEach(effect => {
if (effect !== activeEffect || effect.allowRecurse) {
effects.add(effect)
}
})
}
}
// clear类型的变更,通知所有的依赖项
if (type === TriggerOpTypes.CLEAR) {
// collection being cleared
// trigger all effects for target
depsMap.forEach(add)
} else if (key === 'length' && isArray(target)) {
// 数组长度类型的变更,通知超出新长度的key对应的依赖项
depsMap.forEach((dep, key) => {
if (key === 'length' || key >= (newValue as number)) {
add(dep)
}
})
} else {
// schedule runs for SET | ADD | DELETE
if (key !== void 0) {
add(depsMap.get(key))
}
}
// 定义run方法,执行effect更新
const run = (effect: ReactiveEffect) => {
if (effect.options.scheduler执行) {
// 通过scheduler执行
effect.options.scheduler(effect)
} else {
// 直接执行
effect()
}
}
// 对所有应该通知的依赖项执行run方法
effects.forEach(run)
}
总结:trigger方法根据type确定哪些key对应的依赖项应该被更新,并执行依赖项的更新。