nextTick
是 Vue 中非常重要的一个 API,它允许你在 DOM 更新周期后执行延迟回调。
核心源码位置
Vue3 的 nextTick
实现主要在 packages/runtime-core/src/scheduler.ts
文件中。
基本实现
const resolvedPromise = Promise.resolve() as Promise<any>
let currentFlushPromise: Promise<void> | null = nullexport function nextTick<T = void>(this: T,fn?: (this: T) => void
): Promise<void> {const p = currentFlushPromise || resolvedPromisereturn fn ? p.then(this ? fn.bind(this) : fn) : p
}
关键点解析
-
Promise 基础:
-
使用
Promise.resolve()
创建一个已解决的 Promise 作为基础 -
这样即使没有正在进行的更新,也能返回一个可用的 Promise
-
-
currentFlushPromise:
-
这个变量保存着当前正在进行的更新流程的 Promise
-
如果有更新正在进行,
nextTick
会返回这个 Promise 而不是基础的resolvedPromise
-
-
灵活的参数处理:
-
可以不带参数调用,只返回 Promise
-
可以传入回调函数,回调会在 Promise resolve 后执行
-
支持绑定
this
上下文
-
执行流程
-
当组件状态发生变化时,Vue 会安排一个异步更新队列
-
这个更新队列的执行会设置
currentFlushPromise
-
调用
nextTick
时:-
如果有更新正在进行(
currentFlushPromise
存在),则回调会在这个 Promise 后执行 -
如果没有更新进行,则回调会在微任务队列中立即执行
-
为什么使用 Promise
Vue 3 使用 Promise 作为 nextTick
的实现基础,原因包括:
-
微任务优先级高于宏任务(如 setTimeout),能更早执行
-
现代浏览器广泛支持 Promise
-
可以形成良好的 Promise 链式调用
与 Vue 2 的区别
Vue 2 中 nextTick
的实现更加复杂,有一个降级策略:
-
优先使用 Promise
-
不支持 Promise 时回退到 MutationObserver
-
再不支持则使用 setImmediate
-
最后使用 setTimeout
Vue 3 简化了实现,因为现代浏览器已普遍支持 Promise,不再需要复杂的降级策略。
使用场景
import { nextTick } from 'vue'// 基本用法
nextTick(() => {// DOM 更新后执行
})// 配合 async/await
async function update() {// 修改数据this.message = 'updated'await nextTick()// 现在 DOM 已经更新
}
nextTick
是理解 Vue 响应式系统和更新机制的关键部分,它的简洁实现体现了 Vue 3 对现代 JavaScript 特性的充分利用。