本文主要记录 nextTick api 源码的阅读理解,尽量说清一个执行调度机制,如果理解vue2 中nextTick的实现,应该也能很快上手,换汤不换药,一些叫法/名词不太一样。
先看看调度任务 SchedulerJob
/SchedulerJobs
类型定义:
interface SchedulerJob extends Function {
id?: number // 调度任务权重 - 用于执行顺序控制
pre?: boolean // 调度任务是否在 组件更新 update 前执行
active?: boolean
computed?: boolean // 关联computed
allowRecurse?: boolean // 允许递归
ownerInstance?: ComponentInternalInstance // 组件实例
}
type SchedulerJobs = SchedulerJob | SchedulerJob[] // 任务队列类型
调度任务的执行涉及两个状态字段:
isFlushing
:正在清空调度任务队列(正在执行调度任务)isFlushPending
:清空调度任务队列逻辑 加入微任务队列
全局调度任务队列:
queue: SchedulerJob[] = []
:组件更新前要执行的调度任务队列,flushIndex
记录正在执行的调度任务的索引,控制新的调度任务可以正确加入队列queue,保证正确执行。pendingPostFlushCbs: SchedulerJob[] = []
:组件更新后才执行的任务队列。activePostFlushCbs: SchedulerJob[] | null = null
组件更新后才执行且正处于执行过程的任务队列,pendingPostFlushCbs 经过去重(Set)的任务,postFlushIndex
记录正在执行的post类型调度任务的索引,控制新的post调度任务可以正确加入队列queue,保证正确执行。
加入微任务队列方法:
// 全局 Promise,用于将调度任务加入微任务队列
const resolvedPromise = /*#__PURE__*/ Promise.resolve() as Promise<any>
// 全局 Promise,用于将清空调度任务队列的逻辑加入微任务队列,全局唯一
let currentFlushPromise: Promise<void> | null = null
Vue3异步更新机制直接利用 Promise
机制实现,不像Vue2受限制,需要进行兼容判断使用 MutationObserver/setTimeout/setImmediate
先看看 nextTick
api:
export function nextTick<T = void>(
this: T,
fn?: (this: T) => void // 调度任务
): Promise<void> {
const p = currentFlushPromise || resolvedPromise // 获取Promise,如果已经执行清空调度任务队列,使用currentFlushPromise,否则,直接使用全局 resolvedPromise
return fn ? p.then(this ? fn.bind(this) : fn) : p // 将nextTick调度任务加入微任务队列