vue3 中的响应式 reactivity(二)
effect.ts
class ReactiveEffect() --- effect.ts
ReactiveEffect 的实例就是依赖。deps 里存的 dep 就是 ReactiveEffect的实例, 创建一个包含响应式的副作用函数的对象。
因此ReactiveEffect类就是整个响应式变化执行的核心🌟
export class ReactiveEffect<T = any> {
active = true // 当前是否再run()函数中, 即是是否为嵌套调用
deps: Dep[] = []
computed?: ComputedRefImpl<T> // 是否是computed 创建的
/**
* @internal
*/
allowRecurse?: boolean // 是否允许递归
onStop?: () => void
// dev only
onTrack?: (event: DebuggerEvent) => void
// dev only
onTrigger?: (event: DebuggerEvent) => void
/**
* @internal
*/
_dirtyLevel = DirtyLevels.Dirty
/**
* @internal
*/
_trackId = 0
/**
* @internal
*/
_runnings = 0
/**
* @internal
*/
_shouldSchedule = false
/**
* @internal
*/
_depsLength = 0
constructor(
public fn: () => T, // 副作用函数
public trigger: () => void,
public scheduler?: EffectScheduler,
scope?: EffectScope,
) {
recordEffectScope(this, scope)
}
// 读dirty值时调用
public get dirty() {
if (
this._dirtyLevel === DirtyLevels.MaybeDirty_ComputedSideEffect ||
this._dirtyLevel === DirtyLevels.MaybeDirty
) {
this._dirtyLevel = DirtyLevels.QueryingDirty
pauseTracking()
for (let i = 0; i < this._depsLength; i++) {
const dep = this.deps[i]
if (dep.computed) {
triggerComputed(dep.computed)
if (this._dirtyLevel >= DirtyLevels.Dirty) {
break
}
}
}
if (this._dirtyLevel === DirtyLevels.QueryingDirty) {
this._dirtyLevel = DirtyLevels.NotDirty
}
resetTracking()
}
return this._dirtyLevel >= DirtyLevels.Dirty
}
// 设置dirty时调用, 如 dirty = false时, _dirtyLevel = 0, 否则 = 4
public set dirty(v) {
this._dirtyLevel = v ? DirtyLevels.Dirty : DirtyLevels.NotDirty
}
/**
*
* 1. 如果当前effect实例是不工作状态,就仅仅执行一下fn,不需要收集依赖。
2. 由于在一个effect.run的过程中可能会触发另外的effect.run, 暂存上一次的activeEffect、shouldTrack,
目的是为了本次执行完以后把activeEffect、shouldTrack恢复回去。
3. 设置activeEffect、shouldTrack。
4. runnings自增
6. 执行fn()。
7. 在finally中主要是做一些善后的工作了:移除多余依赖、恢复activeEffect、shouldTrack、
8. 如果deferStop 为 true,执行stop,可能在调用stop时,正在收集依赖,因此推迟到本次收集完成再stop。
*/
run() {
// 每次run函数执行后,_dirtyLevel = 0
this._dirtyLevel = DirtyLevels.NotDirty
if (!this.active) {
// 不活跃状态,直接回调副作用函数
return this.fn()
}
// 因为执行fn的过程中,可能会触发其他effect,形成嵌套调用
// 当前上下文暂存 shouldTrack 和 activeEffect, 释放全局的activeEffect 和 shouldTrack
let lastShouldTrack = shouldTrack
let lastEffect = activeEffect
try {
shouldTrack = true
activeEffect = this
this._runnings++ // 记录正在运行的依赖数量
preCleanupEffect(this) // 当前effect的深度记录为0
return this.fn() // 执行当前副作用函数
} finally {
// 当前副作用函数执行结束,进行一些恢复操作
postCleanupEffect(this)
this._runnings--
activeEffect = lastEffect
shouldTrack = lastShouldTrack
}
}
stop() {
if (this.active) {
preCleanupEffect(this)
postCleanupEffect(this)
this.onStop?.()
this.active = false
}
}
}
trackEffect() --- effect.ts
回忆一下,ref 和 reactive的 get方法最终实现依赖收集的方式都是执行trackEffect.
trackEffect(activeEffect, ref.dep)
export function trackEffect(
effect: ReactiveEffect,
dep: Dep,
debuggerEventExtraInfo?: DebuggerEventExtraInfo,
) {
// 依赖收集, 如果dep中不存在该effect, 执行dep.set()
if (dep.get(effect) !== effect._trackId) {
dep.set(effect, effect._trackId)
// 检查收集的依赖是否需要更新
const oldDep = effect.deps[effect._depsLength]
if (oldDep !== dep) {
if (oldDep) {
cleanupDepEffect(oldDep, effect)
}
effect.deps[effect._depsLength++] = dep
} else {
effect._depsLength++
}
if (__DEV__) {
effect.onTrack?.(extend({ effect }, debuggerEventExtraInfo!))
}
}
}
triggerEffects() --- triggerEffects.ts
export function triggerEffects(
dep: Dep,
dirtyLevel: DirtyLevels,
debuggerEventExtraInfo?: DebuggerEventExtraInfo,
) {
pauseScheduling()
for (const effect of dep.keys()) {
// 遍历dep, 依次执行effect.trigger()方法
// dep.get(effect) is very expensive, we need to calculate it lazily and reuse the result
let tracking: boolean | undefined
if (
effect._dirtyLevel < dirtyLevel &&
(tracking ??= dep.get(effect) === effect._trackId)
) {
effect._shouldSchedule ||= effect._dirtyLevel === DirtyLevels.NotDirty
effect._dirtyLevel = dirtyLevel
}
if (
effect._shouldSchedule &&
(tracking ??= dep.get(effect) === effect._trackId)
) {
if (__DEV__) {
effect.onTrigger?.(extend({ effect }, debuggerEventExtraInfo))
}
effect.trigger() // 确保 computed trigger 先执行
if (
(!effect._runnings || effect.allowRecurse) &&
effect._dirtyLevel !== DirtyLevels.MaybeDirty_ComputedSideEffect
) {
effect._shouldSchedule = false
if (effect.scheduler) {
queueEffectSchedulers.push(effect.scheduler)
}
}
}
}
resetScheduling()
}
一大堆的代码嗷,但核心操作就是
for (const effect of dep.keys()) {
effect.trigger();
}
而 effect.trigger(); 则是在 ReactiveEffect 的构造函数里, 其实就是执行ReactiveEffect.run(),就是执行他的副作用函数.至此,整个响应式基本完成.
3.4 以前是直接调用
effect.run()方法,而3.4以后是调用effect.trigger(),目的是重复调用的bug,确保computed先执行,参考
一些遗漏的点
mutableCollectionHandlers --- collectionHandlers.ts
针对es6 中的 collection 对象进行的封装, 如 Set Map WeakSet WeakMap 的方法进行封装 如对get,has,size,add,size,deleteEntry,clear方法进行track和trigger操作,这里只展示对get方法进行展示
function get(
target: MapTypes,
key: unknown,
isReadonly = false,
isShallow = false,
) {
// #1772: readonly(reactive(Map)) should return readonly + reactive version
// of the value
target = (target as any)[ReactiveFlags.RAW]
const rawTarget = toRaw(target)
const rawKey = toRaw(key)
if (!isReadonly) {
if (hasChanged(key, rawKey)) {
track(rawTarget, TrackOpTypes.GET, key)
}
// 调用get方法时进行依赖收集
track(rawTarget, TrackOpTypes.GET, rawKey)
}
const { has } = getProto(rawTarget)
const wrap = isShallow ? toShallow : isReadonly ? toReadonly : toReactive
if (has.call(rawTarget, key)) {
return wrap(target.get(key))
} else if (has.call(rawTarget, rawKey)) {
return wrap(target.get(rawKey))
} else if (target !== rawTarget) {
// #3602 readonly(reactive(Map))
// ensure that the nested reactive `Map` can do tracking for itself
target.get(key)
}
}