04|更新批处理
第七章 / 04。目标:知道状态已变、组件已入队、DOM 已更新是三个不同时间点。
下面的完整组件可放入第一章 Vite 项目的 src/App.vue,通过 Console 观察日志;watch 时机的前置见 第三章侦听时机。
同步写三次不等于 patch 三次
Vue 把组件更新任务放入队列,并对同一个 job 去重;同一轮同步调用栈中的多次状态修改通常合并到下一次刷新。状态值本身同步可读,DOM 则仍是旧提交:
<script setup lang="ts">
import { nextTick, onUpdated, ref } from 'vue'
const count = ref(0)
let updates = 0
onUpdated(() => console.log('updated hook', ++updates))
async function run() {
const button = document.querySelector<HTMLButtonElement>('#counter')!
console.log('before', { state: count.value, dom: button.textContent })
count.value++
count.value++
count.value++
console.log('same stack', { state: count.value, dom: button.textContent })
await nextTick()
console.log('after nextTick', { state: count.value, dom: button.textContent, updates })
}
</script>
<template><button id="counter" @click="run">{{ count }}</button></template>从 0 点击一次,应看到同一调用栈里 state 已是 3、DOM 仍是 0;nextTick 后 DOM 为 3,onUpdated 本轮只增加一次。开发模式、父子组件关系和其他 watcher 会影响完整队列,不要把“整个应用永远一次更新”当作保证。
组件首次挂载需要立即执行 render 才有 DOM;依赖后来触发时则把同一更新 job 入队。三次写入不是把前两个状态写入取消掉,响应式值确实经历了1、2、3,只是组件渲染在执行时读取最新的3。若把每次写入分到三个 await nextTick() 两侧,就产生三个提交边界,不能再期待一次 onUpdated。
flush 时机是因果关系,不是快慢开关
| 观察者 | 相对组件 DOM 的典型时机 | 用途 |
|---|---|---|
flush: 'pre'(默认) | 自身 DOM 更新前 | 根据新状态发起不依赖新 DOM 的副作用 |
flush: 'post' | DOM 提交后 | 测量更新后的元素、维护焦点 |
flush: 'sync' | 每次触发立即运行,不批处理 | 极少数需要同步观察的简单值 |
sync 监听数组被连续 push 时可能触发很多次;它不是让页面“更及时”的通用优化。nextTick() 等待当前 Vue 更新队列刷完,但不保证浏览器已经完成下一帧绘制;requestAnimationFrame 回调发生在下一次绘制前,可用来把工作安排到下一帧,但也不能当作“已绘制”的证明。几何读取本身可能触发布局,应结合 Performance 时间线判断。
批处理仍可能做很多工作
一次组件 patch 不等于成本很小。如果 render 遍历十万项、watcher 构成级联写入、提交导致同步布局,合并 job 也救不了错误边界。先记录 job/组件更新次数,再在浏览器 Performance 面板区分脚本、布局和绘制。
不要用随机 setTimeout 等 DOM;它依赖机器速度。组件测试应 await trigger()/nextTick(),网络 Promise 再单独等待。
练习:证明队列去重
实现一个按钮,同步把 firstName、lastName、age 各修改一次。记录操作前、同步修改后、await nextTick() 后的 { state, dom, updatedCalls }。合同:同步阶段 DOM 仍是旧值;刷新后显示三个新值;一次点击只产生一次该组件 onUpdated。再加一个 flush: 'sync' watcher,记录它对三次源状态写入的调用次数,并解释为什么不应据此改成同步 DOM 更新。
记录实验结果的变量不要参与该组件模板渲染,也不要在 onUpdated 中修改被模板读取的计数,否则测量动作本身可能引发新一轮更新。自动化断言在完成 第九章测试环境 后回访。
参考答案:第 7 章第 04 题。