前文
在之前其实我是很少写文章的, 都是读完就完了, 不过最近面试多了发现, 就像我的签名说的, 有些事情你写不出来就说明你没有想清楚. 而只是读完的时候, 我确实是还没有想清楚..
进入正题
首先解释一下我解读源码的方式, 首先当前的大部分代码都是互相嵌套调用的, 所以很多时候我们并没有办法找出线头最开始的地方, 所以我的方式就是从引用最少的地方开始, 一步步抽丝剥茧地找到它的下一步, 再下一步, 直到将所有的内容组合起来, 形成整体. 最后得到我们的理解.
结合官网上面的api用法, 进行对应的解读.
1. 核心
reactivity/src/dep.ts, 依赖收集的实例辅助字段, 主要用于处理依赖收集后的清除旧依赖.
export type Dep = Set<ReactiveEffect> & TrackedMarkers
// w和n用于重新依赖收集后清除不再依赖的effect
type TrackedMarkers = {
w: number // 是否track, 通过& trackOpBit 判断
n: number // 是否新track, 通过& trackOpBit 判断
}
// 设置dep在上一次已经track, 在触发新一轮依赖收集前使用
export const initDepMarkers = ({ deps }: ReactiveEffect) => {
if (deps.length) {
for (let i = 0; i < deps.length; i++) {
deps[i].w |= trackOpBit // set was tracked
}
}
}
// 重新进行依赖收集后, 根据w和n值, 清除已经不再依赖的effect, 然后清除标记
export const finalizeDepMarkers = (effect: ReactiveEffect) => {
const { deps } = effect
if (deps.length) {
let ptr = 0
for (let i = 0; i < deps.length; i++) {
const dep = deps[i]
if (wasTracked(dep) && !newTracked(dep)) {
dep.delete(effect)
} else {
deps[ptr++] = dep
}
// clear bits
dep.w &= ~trackOpBit
dep.n &= ~trackOpBit
}
deps.length = ptr
}
}
reactivity/src/effect.ts 依赖收集的的实例.
vue的依赖收集想必都知道, 首先就是依赖收集的依赖实现函数, 在依赖函数中主要做了两件事情.
- 在执行时, 缓存上一个activeEffect并指定当前为active, 然后执行回调函数并且进行依赖收集. 依赖收集完成后 清除旧的依赖内容并且恢复activeEffect
- 停止并且清除依赖.
ReactiveEffec是被收集的依赖. 被收集时将其添加到对应的deps列表中.
// 依赖收集中的依赖实现函数.
export class ReactiveEffect<T = any> {
active = true
deps: Dep[] = []
parent: ReactiveEffect | undefined = undefined
// 如果是一个computed函数, 则会有此属性
computed?: ComputedRefImpl<T>
allowRecurse?: boolean
private deferStop?: boolean
onStop?: () => void
// dev only
onTrack?: (event: DebuggerEvent) => void
// dev only
onTrigger?: (event: DebuggerEvent) => void
constructor(
public fn: () => T,
public scheduler: EffectScheduler | null = null,
scope?: EffectScope
) {
recordEffectScope(this, scope)
}
// 执行effect
run() {
if (!this.active) {
// 如果已经触发了stop, 则立刻执行, 不触发依赖收集, 用于在先触发了stop后异步触发run的情况
return this.fn()
}
let parent: ReactiveEffect | undefined = activeEffect
let lastShouldTrack = shouldTrack
// 获取当前
while (parent) {
if (parent === this) {
return
}
parent = parent.parent
}
try {
// 临时记录activeEffect
this.parent = activeEffect
activeEffect = this
shouldTrack = true
// 递归track的层级
trackOpBit = 1 << ++effectTrackDepth
if (effectTrackDepth <= maxMarkerBits) {
initDepMarkers(this)
} else {
// 层级过多时先清除再收集依赖的方式进行处理.
cleanupEffect(this)
}
return this.fn()
} finally {
if (effectTrackDepth <= maxMarkerBits) {
// 如果使用init的方式进行收集, 此时需要去掉旧的收集内容.
finalizeDepMarkers(this)
}
trackOpBit = 1 << --effectTrackDepth
// 恢复activeEffect
activeEffect = this.parent
shouldTrack = lastShouldTrack
this.parent = undefined
// 如果设置了延后清除, 则再执行一次清除.
if (this.deferStop) {
this.stop()
}
}
}
// 停止时如果正在运行, 则延后清除.
stop() {
// stopped while running itself - defer the cleanup
if (activeEffect === this) {
this.deferStop = true
} else if (this.active) {
cleanupEffect(this)
if (this.onStop) {
this.onStop()
}
this.active = false
}
}
}
依赖收集函数, 在执行watcher的条件或者computed时, 设置activeEffect, 然后在对应的变量触发get时, 添加dep到activeEffect的deps中, 同时添加activeEffect到对应的变量上.
/** 收集依赖, 先预处理依赖map后, 添加到依赖队列 */
export function track(target: object, type: TrackOpTypes, key: unknown) {
if (shouldTrack && activeEffect) {
let depsMap = targetMap.get(target)
if (!depsMap) {
targetMap.set(target, (depsMap = new Map()))
}
let dep = depsMap.get(key)
if (!dep) {
depsMap.set(key, (dep = createDep()))
}
const eventInfo = __DEV__
? { effect: activeEffect, target, type, key }
: undefined
trackEffects(dep, eventInfo)
}
}
/** 收集依赖, 添加到依赖队列中, activeEffect表示正在执行的副作用函数 */
export function trackEffects(
dep: Dep,
debuggerEventExtraInfo?: DebuggerEventExtraInfo
) {
let shouldTrack = false
if (effectTrackDepth <= maxMarkerBits) {
if (!newTracked(dep)) {
dep.n |= trackOpBit // set newly tracked
shouldTrack = !wasTracked(dep)
}
} else {
// Full cleanup mode.
shouldTrack = !dep.has(activeEffect!)
}
if (shouldTrack) {
dep.add(activeEffect!)
activeEffect!.deps.push(dep)
if (__DEV__ && activeEffect!.onTrack) {
activeEffect!.onTrack({
effect: activeEffect!,
...debuggerEventExtraInfo!
})
}
}
}
触发回调函数与依赖收集刚好对应
/** 触发回调 */
export function trigger(
target: object,
type: TriggerOpTypes,
key?: unknown,
newValue?: unknown,
oldValue?: unknown,
oldTarget?: Map<unknown, unknown> | Set<unknown>
) {
const depsMap = targetMap.get(target)
if (!depsMap) {
// never been tracked
return
}
let deps: (Dep | undefined)[] = []
if (type === TriggerOpTypes.CLEAR) {
// collection being cleared
// trigger all effects for target
deps = [...depsMap.values()]
} else if (key === 'length' && isArray(target)) {
const newLength = toNumber(newValue)
depsMap.forEach((dep, key) => {
if (key === 'length' || key >= newLength) {
deps.push(dep)
}
})
} else {
// schedule runs for SET | ADD | DELETE
if (key !== void 0) {
deps.push(depsMap.get(key))
}
// also run for iteration key on ADD | DELETE | Map.SET
switch (type) {
case TriggerOpTypes.ADD:
if (!isArray(target)) {
deps.push(depsMap.get(ITERATE_KEY))
if (isMap(target)) {
deps.push(depsMap.get(MAP_KEY_ITERATE_KEY))
}
} else if (isIntegerKey(key)) {
// new index added to array -> length changes
deps.push(depsMap.get('length'))
}
break
case TriggerOpTypes.DELETE:
if (!isArray(target)) {
deps.push(depsMap.get(ITERATE_KEY))
if (isMap(target)) {
deps.push(depsMap.get(MAP_KEY_ITERATE_KEY))
}
}
break
case TriggerOpTypes.SET:
if (isMap(target)) {
deps.push(depsMap.get(ITERATE_KEY))
}
break
}
}
const eventInfo = __DEV__
? { target, type, key, newValue, oldValue, oldTarget }
: undefined
if (deps.length === 1) {
if (deps[0]) {
if (__DEV__) {
triggerEffects(deps[0], eventInfo)
} else {
triggerEffects(deps[0])
}
}
} else {
const effects: ReactiveEffect[] = []
for (const dep of deps) {
if (dep) {
effects.push(...dep)
}
}
if (__DEV__) {
triggerEffects(createDep(effects), eventInfo)
} else {
triggerEffects(createDep(effects))
}
}
}
/** 触发回调计算, 先计算computed属性, 再计算别的属性 */
export function triggerEffects(
dep: Dep | ReactiveEffect[],
debuggerEventExtraInfo?: DebuggerEventExtraInfo
) {
const effects = isArray(dep) ? dep : [...dep]
for (const effect of effects) {
if (effect.computed) {
triggerEffect(effect, debuggerEventExtraInfo)
}
}
for (const effect of effects) {
if (!effect.computed) {
triggerEffect(effect, debuggerEventExtraInfo)
}
}
}
未完待续...