Vue 在响应式状态变化后调度组件更新,并把新的渲染结果应用到真实 DOM。状态赋值通常不会在同一行代码中立刻完成 DOM 更新;Vue 会缓冲同一轮中的更新,以避免一个组件因连续多次状态变化而重复更新。
更新批处理
<script setup>
import { ref, nextTick } from 'vue'
const count = ref(0)
async function increment() {
count.value++
// 此处 DOM 通常仍是更新前的内容
await nextTick()
// 此处相关 DOM 更新已经完成
}
</script>
nextTick() 返回一个 Promise,也可以接收回调。它用于等待当前一批待处理的 DOM 更新完成,而不是强制每一次状态赋值立即同步修改 DOM。
从状态变化到 DOM
一轮典型更新包含以下关系:
修改响应式状态
↓
触发依赖该状态的组件更新
↓
调度器合并本轮任务
↓
重新执行渲染并更新虚拟 DOM
↓
将必要变化 patch 到真实 DOM
组件重新渲染不等于整棵真实 DOM 重建。渲染器会根据新旧虚拟节点及编译器提供的信息执行文本、属性、事件监听、节点插入、移动或删除等必要操作。
DOM 更新后的操作
需要读取更新后 DOM 时,可以等待 nextTick(),也可以在适当的生命周期钩子中处理。onUpdated() 会在组件因响应式状态变化完成 DOM 更新后调用,但一次父子组件更新中,子组件的 updated 钩子先于父组件。若只关心某一次特定状态变化,nextTick() 通常能更直接地限定时机。
侦听器也有不同的执行时机。默认侦听器回调发生在所属组件 DOM 更新之前;需要访问更新后的 DOM 时,可以使用 flush: 'post' 或相应的 post-effect API。同步侦听器不会批处理,应避免用于可能被频繁同步修改的数据源。
模板引用
模板引用允许在组件挂载后访问 DOM 元素或子组件实例,适合聚焦、测量或接入第三方库。直接 DOM 操作应限制在必要的命令式边界内;由 Vue 管理的界面内容仍应通过响应式状态和模板表达。
相关官方资料:nextTick()、组件生命周期、侦听器的回调触发时机、模板引用。