Vue 中的 $nextTick 是什么?什么时候用?

79 阅读1分钟

在写 Vue 项目的过程中,你可能遇到过这样的情况:明明刚刚改了数据,但紧接着读取 DOM 时,拿到的还是旧的状态。这时候就需要用到 $nextTick。

1、先理解 Vue 更新 DOM 的节奏

Vue 有一个核心特性:数据变了,DOM 不会立刻更新。

打个比方:Vue 就像一个餐厅的厨师,你(JS 代码)是点单的服务员。

  • 你说「把 isLoading 改成 false」→ 厨师把这个任务记到小本子上
  • 你说「再把 title 改成 '加载完成'」→ 厨师继续记
  • 等你这一轮点单说完了(当前这段 JS 执行完毕)→ 厨师才一次性把所有变化都做到 DOM 上

这个"一次性处理"的动作,就叫做一次 tick(更新批次) 。

Vue 这么设计是为了性能——如果你改了 10 个数据,不用刷新 DOM 10 次,合并成 1 次就够了(关于这个设计的详细原因,可以看《Vue 为什么选择异步更新 DOM》这篇文章)。

2、$nextTick 是什么

$nextTick(callback) 的意思就是:

"等厨师把这一批菜都做完、端上桌之后,再执行我的回调。"

注意关键词是这一批:

  • ✅ 等:Vue 把当前已排队的 DOM 更新全部刷新完
  • ❌ 不等:接口请求返回、图片加载完成、CSS 动画结束

3、用代码来感受一下

不用 $nextTick 的问题:

export default {
  data() {
    return {
      showBox: false
    }
  },
  methods: {
    handleClick() {
      this.showBox = true
      // ❌ 此时 DOM 还没更新,box 元素可能还不存在
      const box = document.querySelector('.box')
      console.log(box) // null 或者旧状态
    }
  }
}

用 $nextTick 修复:

methods: {
  handleClick() {
    this.showBox = true
    // ✅ 等 Vue 把 DOM 更新完,再读取元素
    this.$nextTick(() => {
      const box = document.querySelector('.box')
      console.log(box) // 正确拿到元素
    })
  }
}

另一个常见场景——展开折叠后检测元素位置:

methods: {
  expandPanel() {
    this.isExpanded = true
    // 此时 DOM 里折叠区域还没展开

    this.$nextTick(() => {
      // 现在 DOM 已经更新,可以准确读取元素位置
      const el = this.$refs.panel
      console.log(el.getBoundingClientRect())
    })
  }
}

如果不加 $nextTick,直接读 getBoundingClientRect(),拿到的还是折叠状态的尺寸,是错误的数据。

4、执行顺序拆解

① this.isExpanded = true      ← 改数据,Vue 记入队列
② this.title = '已展开'        ← 改数据,Vue 记入队列
③ $nextTick(callback) 登记     ← 告诉 Vue:"队列刷完叫我"

--- 当前这段 JS 执行完毕 ---

④ Vue 把 ①② 产生的所有 DOM 变化一次性刷新
⑤ 执行 callback(你的代码)

callback 里的逻辑一定在 DOM 更新之后执行,这是 Vue 的保证。

5、什么时候需要用 $nextTick

场景是否需要 $nextTick
改完数据后,需要读取 DOM 尺寸/位置✅ 需要
改完数据后,需要操作 DOM 元素(聚焦、滚动等)✅ 需要
改完数据后,需要通过 $refs 调用子组件方法✅ 需要
只是改数据,不需要立刻读 DOM❌ 不需要
等接口返回后更新数据❌ 不需要(接口回调本身就是异步的)

6、Vue 3 中的写法

Vue 3 中用法基本相同,也可以用 await 方式:

import { nextTick } from 'vue'

async function handleClick() {
  isExpanded.value = true
  await nextTick()
  // 此时 DOM 已经更新
  console.log(el.value.getBoundingClientRect())
}

7、一句话总结

$nextTick 是 Vue 提供的一个时机钩子,让你能在"数据改完 → DOM 更新完毕"这个节点插入自己的逻辑。它等的不是所有异步操作,只等 Vue 自己的这一轮 DOM 刷新。