Q15
★ ★ ★ ★ ★
Vue 3 的 nextTick 是如何实现的?
⚡ 速记答案(30 秒)
把回调放进一个"DOM 更新后再执行"的队列。队列依赖微任务(优先用 Promise.then),保证调用时所有同步状态更新和 DOM patch 已完成。
📖 详细讲解
标准面试回答(推荐记住)
nextTick 将回调函数放入微任务队列,确保在当前事件循环的 DOM 更新完成后执行。Vue 3 优先使用 Promise.then 实现,回退到 MutationObserver 或 setTimeout。这样可以保证在 nextTick 回调中访问到更新后的 DOM。
微任务队列与 DOM 更新
Vue 的 DOM 更新是异步的。当你修改数据时,Vue 不会立即触碰 DOM,而是:
- 开启一个队列。
- 将所有发生的变更加入队列(去重)。
- 如果队列不为空,通过
nextTick(内部使用 Promise.then) 注册一个微任务来flushJobs。
nextTick() 原理:
用户调用 nextTick(fn),实际上是将 fn 放到微任务队列的末尾。
因为 flushJobs 也是一个微任务,且它是先被添加的,所以根据微任务队列先进先出的特性,flushJobs 会先执行(完成 DOM 更新),然后才轮到用户的 fn 执行。
Event Loop 顺序:
Code Execution -> Sync Tasks -> Microtasks (DOM Update -> nextTick callbacks) -> UI Render
✅ 面试要点
- •理解 nextTick 解决的问题
- •知道微任务队列的概念
- •掌握正确的使用时机
💻 代码示例
nextTick 使用场景
import { ref, nextTick } from 'vue'
const message = ref('Hello')
const inputRef = ref<HTMLInputElement>()
async function updateAndFocus() {
message.value = 'Updated'
// 方式1: Promise 方式
await nextTick()
inputRef.value?.focus()
// 方式2: 回调方式
// nextTick(() => {
// inputRef.value?.focus()
// })
}
// nextTick 简化实现
const queue: Function[] = []
let pending = false
function nextTick(fn?: () => void) {
return fn
? Promise.resolve().then(fn)
: Promise.resolve()
}