Vue 将 patch 放入微任务以合并多次数据变更,确保一次 DOM 更新并准确获取新状态;nextTick 等待 patch 完成而非浏览器渲染,回调中可安全读取更新后的 DOM。

因为 Patch 本身是同步的,但把它放进微任务,才能让多次数据变更合并成一次 DOM 更新,并确保开发者能准确拿到更新后的 DOM 状态。
避免重复渲染,提升性能
连续修改响应式数据时(比如 this.count++ 执行三次),Vue 不会立刻执行三次 patch,而是把对应的更新任务推入队列,等同步代码跑完,再统一在微任务里 flush 队列、执行一次 patch。这样真实 DOM 只更新一次,而不是三次,省去了中间无效状态和反复重排重绘。
- 同一事件循环内所有数据变更,只触发一轮 patch
- 子组件不会因父组件多次赋值而被重复更新
- 浏览器不用为中间值做 layout 或 paint
保证 DOM 更新与 JavaScript 执行的合理顺序
微任务在当前宏任务结束、浏览器渲染前执行。这意味着:
- 所有同步逻辑(如事件处理、计算属性求值)先完成
- patch 同步执行并完成真实 DOM 修改
- nextTick 回调紧随其后,在渲染发生前运行
- 此时读取 ref.value.offsetWidth 或 $el.innerHTML 拿到的是新 DOM 的真实值
NextTick 的回调不是“等渲染完成”,而是“等 patch 完成”
很多人误以为 nextTick 是等浏览器画完帧,其实它等的是 Vue 内部的 patch 函数执行完毕。DOM 操作本身是同步的,只是时机被调度到了微任务里。所以:
- 在 nextTick 回调里操作 DOM 是安全的——vnode 已打完补丁,真实节点已就位
- 但此时浏览器尚未 repaint,所以视觉上还没变;若需确保画面刷新后再执行,应配合 requestAnimationFrame
- 多个 nextTick 调用会被合并进同一批微任务,不会重复排队
底层依赖微任务的执行优先级
Vue 优先使用 Promise.then,降级用 MutationObserver,最后才 fallback 到 setTimeout。这是为了严格卡在:
- 同步代码之后
- 浏览器 UI 渲染之前
- 下一个宏任务(如 setTimeout)之前
这个时间窗口,刚好是 patch 完成 + DOM 属性可读 + 视图未刷新的黄金时机。

















