📚前端面试题速记
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,而是:

  1. 开启一个队列。
  2. 将所有发生的变更加入队列(去重)。
  3. 如果队列不为空,通过 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()
}