共计 4257 个字符,预计需要花费 11 分钟才能阅读完成。
【vue3 源码】六、scheduler
在前文剖析
watch
的过程中,咱们晓得在effect
的调度器中会将job
推入不同的工作队列,以在不同机会执行job
函数。本文将深入分析job
的执行机会。
在 watch
中,会依据 flush
对scheduler
进行不同解决。如果 flush
是sync
,代表同步,那么 scheduler
就是 job
,在依赖触发时,会间接执行job
;如果scheduler
是post
,在依赖触发时,会调用一个 queuePostRenderEffect
函数;而默认状况,在依赖触发时,会依据以后有无组件实例,进行不同操作。
if (flush === 'sync') {scheduler = job as any} else if (flush === 'post') {scheduler = () => queuePostRenderEffect(job, instance && instance.suspense)
} else {scheduler = () => {if (!instance || instance.isMounted) {queuePreFlushCb(job)
} else {job()
}
}
}
queuePostRenderEffect
export const queuePostRenderEffect = __FEATURE_SUSPENSE__
? queueEffectWithSuspense
: queuePostFlushCb
如果开启了 __FEATURE_SUSPENSE__
了,queuePostRenderEffect
是queueEffectWithSuspense
,否则是queuePostFlushCb
。这里为了不便了解,咱们只看queuePostFlushCb
。
queuePostFlushCb
export function queuePostFlushCb(cb: SchedulerJobs) {queueCb(cb, activePostFlushCbs, pendingPostFlushCbs, postFlushIndex)
}
queuePostFlushCb
接管一个 SchedulerJobs
类型 cb
参数,对于 SchedulerJobs
的定义:
export interface SchedulerJob extends Function {
// 一个标识
id?: number
// 是否为激活状态
active?: boolean
computed?: boolean
allowRecurse?: boolean
ownerInstance?: ComponentInternalInstance
}
export type SchedulerJobs = SchedulerJob | SchedulerJob[]
在 queuePostFlushCb
外部调用了一个 queueCb
办法,并传入了四个变量,其中第一个是 queuePostFlushCb
的参数,而后三个是全局变量
const pendingPostFlushCbs: SchedulerJob[] = []
let activePostFlushCbs: SchedulerJob[] | null = null
let postFlushIndex = 0
接着看queueCb
queueCb
function queueCb(
cb: SchedulerJobs,
activeQueue: SchedulerJob[] | null,
pendingQueue: SchedulerJob[],
index: number
) {if (!isArray(cb)) {
if (
!activeQueue ||
!activeQueue.includes(cb, cb.allowRecurse ? index + 1 : index)
) {pendingQueue.push(cb)
}
} else {pendingQueue.push(...cb)
}
queueFlush()}
queueCb
函数接管四个参数:cb
、activeQueue
(激活的队列)、pendingQueue
(期待中的队列)、index
(一个索引),
如果 cb
是数组,会将 cb
解构放入 pendingQueue
中;否则判断是否传入 activeQueue
或activeQueue
中是否曾经存在 cb
(如果cb.allowRecurse
为true
,从 index+1
处开始寻找,否则从 index
处开始寻找)。最初执行一个 queueFlush
函数。
queueFlush
// 以后是否有正在执行的工作
let isFlushing = false
// 以后是否有正在期待执行的工作
let isFlushPending = false
function queueFlush() {if (!isFlushing && !isFlushPending) {
isFlushPending = true
// 正在执行的 promise
currentFlushPromise = resolvedPromise.then(flushJobs)
}
}
queueFlush
中的逻辑比较简单。如果没有正在执行的工作并且也没有正在期待中的工作,则将 isFlushPending
置为 true
,同时将flushJobs
放入一个微工作队列。
flushJobs
function flushJobs(seen?: CountMap) {
// 将 isFlushPending 置为 false,因为曾经进入执行工作中的状态
isFlushPending = false
// 正在执行工作
isFlushing = true
if (__DEV__) {seen = seen || new Map()
}
// 执行 pendingPreFlushCbs 中的工作
flushPreFlushCbs(seen)
// 将 queue 按 job.id 升序
queue.sort((a, b) => getId(a) - getId(b))
const check = __DEV__
? (job: SchedulerJob) => checkRecursiveUpdates(seen!, job)
: NOOP
try {
// 执行 queue 中的工作
for (flushIndex = 0; flushIndex < queue.length; flushIndex++) {const job = queue[flushIndex]
if (job && job.active !== false) {if (__DEV__ && check(job)) {continue}
callWithErrorHandling(job, null, ErrorCodes.SCHEDULER)
}
}
} finally {
// 重置 flushIndex
flushIndex = 0
// 清空 queue
queue.length = 0
// 执行 pendingPostFlushCbs 中的工作
flushPostFlushCbs(seen)
// 以后没有正在执行的工作
isFlushing = false
currentFlushPromise = null
// 执行残余工作
if (
queue.length ||
pendingPreFlushCbs.length ||
pendingPostFlushCbs.length
) {flushJobs(seen)
}
}
}
在 flushJobs
中会顺次执行 pendingPreFlushCbs
、queue
、pendingPostFlushCbs
中的工作。
再来看queuePreFlushCb
。
queuePreFlushCb
const pendingPreFlushCbs: SchedulerJob[] = []
let activePreFlushCbs: SchedulerJob[] | null = null
let preFlushIndex = 0
export function queuePreFlushCb(cb: SchedulerJob) {queueCb(cb, activePreFlushCbs, pendingPreFlushCbs, preFlushIndex)
}
queuePreFlushCb
办法会将 cb
放入一个 pendingPreFlushCbs
数组。
通过前文咱们晓得最终 pendingPreFlushCbs
、queue
、pendingPostFlushCbs
会按程序顺次执行,pendingPreFlushCbs
中保留的是 flush===pre
时的 job
,pendingPostFlushCbs
中保留的是 flush===post
时的 job
。那么queue
中是什么呢?queue
中保留的组件的更新函数。
const effect = (instance.effect = new ReactiveEffect(
componentUpdateFn,
() => queueJob(instance.update),
instance.scope
))
queueJob
export function queueJob(job: SchedulerJob) {
// queue.length 为 0 或 queue 中不存在 job,并且 job 不等于 currentPreFlushParentJob
if (
(!queue.length ||
!queue.includes(
job,
isFlushing && job.allowRecurse ? flushIndex + 1 : flushIndex
)) &&
job !== currentPreFlushParentJob
) {if (job.id == null) {queue.push(job)
} else {
// 替换反复 id 的 job
queue.splice(findInsertionIndex(job.id), 0, job)
}
queueFlush()}
}
nextTick
export function nextTick<T = void>(
this: T,
fn?: (this: T) => void
): Promise<void> {
const p = currentFlushPromise || resolvedPromise
// 将 fn 退出到一个微工作队列,它会在 p 执行完之后执行
return fn ? p.then(this ? fn.bind(this) : fn) : p
}
在每次向 pendingPreFlushCbs
、queue
、pendingPostFlushCbs
中放入工作时,都会执行 queueFlush()
办法,queueFlush
办法会更新 currentFlushPromise
为最新的 promise
。所以应用nextTick
传入的函数会在 flushJobs
之后执行。