Vue 中 nextTick 的作用是什么?原理是什么?

作用

Vue 的 DOM 更新是异步的(批量处理),nextTick 用于在 DOM 更新完成后执行回调。

import { ref, nextTick } from 'vue'

const message = ref('')

async function updateMessage() {
  message.value = 'updated'
  console.log(document.getElementById('msg').textContent) // '' (DOM 还没更新)

  await nextTick()
  console.log(document.getElementById('msg').textContent) // 'updated' (DOM 已更新)
}

为什么需要 nextTick

Vue 将数据变化后的 DOM 更新放到微任务队列中批量执行,避免频繁操作 DOM:

数据变化 → 加入更新队列 → 当前同步代码继续执行 → 微任务执行 → DOM 更新

所以数据变化后立即读取 DOM 拿到的是旧值,需要 nextTick 等待 DOM 更新。

使用场景

1. 数据变化后操作 DOM

const showInput = ref(false)

async function toggleAndFocus() {
  showInput.value = true
  await nextTick()
  // DOM 更新后才能获取到 input 元素
  inputRef.value.focus()
}

2. 获取更新后的元素尺寸

const items = ref([])

async function addItem() {
  items.value.push(newItem)
  await nextTick()
  // 获取列表最新高度
  const height = listEl.value.scrollHeight
  listEl.value.scrollTop = height  // 滚动到底部
}

3. 确保子组件已更新

const data = ref([])

async function refresh() {
  data.value = await fetchData()
  await nextTick()
  // 子组件已用新数据渲染完成
  childRef.value.doSomething()
}

原理

Vue 的更新机制使用微任务(Promise.then):

// 简化版
const callbacks = []
let pending = false

function flushCallbacks() {
  pending = false
  const copies = callbacks.slice()
  callbacks.length = 0
  copies.forEach(cb => cb())
}

function nextTick(cb) {
  callbacks.push(cb)
  if (!pending) {
    pending = true
    Promise.resolve().then(flushCallbacks) // 微任务
  }
}

// Vue 内部数据变化时也会调用 nextTick 安排更新
function queueUpdate() {
  nextTick(flushUpdateQueue)
}

Vue 的异步更新策略

// 多次修改数据,只触发一次更新
state.count = 1
state.count = 2
state.count = 3
// 最终只渲染一次,count = 3

Vue2 vs Vue3

同分类其他题目