Vue中的nextTick

Vue 在批量更新 DOM 后等待视图完成刷新的 nextTick 机制及其使用场景。

#tech / dev / frame #resource / vue3 #type / concept #status / growing

[!info] related notes

Vue中的nextTick

一句话定义

nextTick 返回一个 Promise,在 Vue 完成本轮 DOM 更新后才执行回调,用于在状态变更后读取最新 DOM。

核心机制

为什么需要 nextTick

Vue 的响应式更新是异步批量的。当你修改状态时,Vue 不会立刻更新 DOM,而是把更新推入一个异步队列,在同一个事件循环 tick 中的多次状态变更会被合并为一次 DOM 更新。

<script setup>
import { ref, onMounted } from 'vue'

const count = ref(0)

function increment() {
  count.value++
  // 此时 DOM 还没有更新
  console.log(document.getElementById('counter').textContent)  // "0"
}
</script>

<template>
  <div id="counter">{{ count }}</div>
  <button @click="increment">+1</button>
</template>

nextTick 的底层机制

Vue 3 的 nextTick 内部优先使用 Promise.resolve() 创建微任务:

// 简化实现
const resolvedPromise = Promise.resolve()

function nextTick(fn) {
  return fn ? resolvedPromise.then(fn) : resolvedPromise
}

这意味着 nextTick 的回调在当前同步代码执行完毕、微任务队列开始处理时执行,正好排在 Vue 的 DOM 更新之后。

微任务队列时序

同步代码执行

count.value++ → 推入更新队列

console.log(DOM) → 读到旧 DOM(更新还没执行)

同步代码结束

微任务队列开始

Vue 执行 DOM 更新(flush jobs)

nextTick 回调执行 → 读到新 DOM

代码示例

基本用法

<script setup>
import { ref, nextTick } from 'vue'

const message = ref('旧消息')
const el = ref(null)

async function update() {
  message.value = '新消息'

  // DOM 还没更新
  console.log(el.value.textContent)  // "旧消息"

  await nextTick()

  // DOM 已更新
  console.log(el.value.textContent)  // "新消息"
}
</script>

<template>
  <div ref="el">{{ message }}</div>
  <button @click="update">更新</button>
</template>

更新列表后滚动到底部

<script setup>
import { ref, nextTick } from 'vue'

const messages = ref([])
const listEl = ref(null)

async function addMessage(text) {
  messages.value.push(text)
  await nextTick()
  listEl.value.scrollTop = listEl.value.scrollHeight
}
</script>

打开弹窗后聚焦输入框

<script setup>
import { ref, nextTick } from 'vue'

const visible = ref(false)
const inputEl = ref(null)

async function openDialog() {
  visible.value = true
  await nextTick()
  inputEl.value.focus()
}
</script>

nextTick 与 setTimeout 的区别

nextTick 是微任务,在 Vue 更新队列 flush 之后立即执行。setTimeout 是宏任务,排在所有微任务之后,时机更晚且不精确。需要等 DOM 更新时,始终用 nextTick

边界与常见误解

  • nextTick 不是”万能延迟”:它只等一轮 DOM 更新,不是 setTimeout 的替代品。
  • nextTick 的回调在微任务队列中执行:如果在 nextTick 回调中再次修改状态,新的更新会在下一轮微任务中处理。
  • 多个 nextTick 调用会合并:连续调用 nextTick(fn1)nextTick(fn2),fn1 和 fn2 都会在同一轮 DOM 更新后执行。
  • Vue 2 中 nextTick 是全局方法和实例方法this.$nextTick()Vue.nextTick()。Vue 3 中推荐使用导入的 nextTick() 函数。
  • watchEffect / watch 的 flush 选项watchEffect(fn, { flush: 'post' }) 等价于在 DOM 更新后执行,可以替代部分 nextTick 场景。

面试要点

来自 vue-next-tick-interview-question 的面试视角整理。

一句话回答

nextTick 用来等待 Vue 把本轮状态变化对应的 DOM 更新真正刷完,适合那些必须依赖最新 DOM 的场景。

最稳的回答主线

  • Vue 会批量调度 DOM 更新,同一 tick 中的多次状态变更合并为一次更新
  • 所以改完状态后,马上读 DOM 不一定是新结果
  • 如果后续逻辑依赖最新 DOM,就用 nextTick
  • 底层基于 Promise.resolve() 微任务,在 Vue 更新队列 flush 之后执行
创建于 2026/3/19 更新于 2026/5/27