vue默认用微任务(如promise.then)执行dom更新,因其在同步代码结束后立即执行、清空队列且不触发重绘;仅当环境不支持微任务或需兼容ios webview、规避过载、衔接动画等场景时才降级使用宏任务。

Vue.js 的响应式更新不是“改完立刻渲染”,而是把更新任务放进队列,等合适时机再批量执行。这个“合适时机”由微任务和宏任务共同决定——微任务优先执行,保证更新及时;宏任务兜底,确保兼容性与可控性。
为什么 Vue 要用微任务做默认更新
微任务(如 Promise.then、MutationObserver)在当前同步代码执行完后立即运行,且会清空整个微任务队列。Vue 利用这一点,在数据变化后不马上操作 DOM,而是把更新函数推入微任务队列,等所有同步逻辑结束、DOM 还没重绘前就完成视图刷新。
- 避免多次触发重排重绘:同一事件循环内多次修改数据,只产生一次微任务,最终只刷新一次 DOM
- 保证 DOM 更新与用户交互节奏一致:比如在 click 回调中修改数据并调用
this.$nextTick,回调能拿到刚更新的 DOM 结构 - 天然支持链式调用:多个
nextTick会排队进入同一个微任务队列,按顺序执行
宏任务在什么情况下被启用
当环境不支持 Promise 或 MutationObserver 时,Vue 会降级使用宏任务(如 setTimeout、setImmediate)。此外,开发者也可主动触发宏任务调度——例如用 withMacroTask 包装的事件处理器,或显式调用 nextTick(..., { flush: 'macro' })(Vue 3.4+)。
- 兼容老版本 iOS WebView:Promise 存在 bug,MutationObserver 不稳定,此时 setTimeout 成为可靠 fallback
- 需要延迟到下一轮宏任务再执行:比如希望 UI 渲染先发生,再做后续逻辑(如动画衔接、第三方库初始化)
- 规避微任务过载风险:极端场景下大量
nextTick堆积可能阻塞主线程,宏任务提供更宽松的调度窗口
nextTick 的实际调度流程
Vue 的 nextTick 并非简单封装 Promise,而是一套带优先级判断和状态管理的调度器。它内部维护一个回调数组 callbacks 和一个开关 pending,确保同一轮事件循环中多次调用只注册一次任务。
- 首次调用时,若未处于 pending 状态,则将 flush 函数包装为微任务(或宏任务)并触发
- 后续调用只往
callbacks推入新回调,不重复触发调度 - flush 执行时,拷贝当前 callbacks 数组并清空原数组,再逐个执行——这保证了回调执行期间新增的 nextTick 不会干扰本轮
开发中如何选择微任务还是宏任务
日常开发几乎不需要手动指定任务类型。但遇到特定场景时,理解差异能帮你写出更可靠的逻辑:
- 读取刚更新的 DOM 尺寸/位置 → 用
nextTick(默认微任务)即可,它在渲染前执行 - 等待浏览器真正完成渲染(比如动画帧对齐)→ 改用
requestAnimationFrame或setTimeout(..., 0) - 集成某些依赖“渲染后回调”的库(如部分图表组件)→ 查文档确认是否要求宏任务时机,必要时手动降级
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











