Vue.nextTick 的核心作用是 在 DOM 更新完成后执行回调,它的原理和事件循环机制(Event Loop) 以及 微任务(Microtask) 紧密相关。
为什么需要 nextTick?
Vue 的数据更新是异步的,也就是:
- 
当你修改 data 时,Vue 并不会立刻更新 DOM 
- 
它会将更新操作 推入一个队列,在 同一事件循环中合并多次修改,最后一次性更新 DOM(提升性能) 
所以:
- 
如果你想在修改数据后,拿到最新的 DOM,就要等更新完成 
- 
这就是 Vue.nextTick 的作用 
nextTick 的原理
nextTick 的核心逻辑:
- 
把回调放进一个任务队列中 
- 
选择一个合适的时机,在 DOM 更新完成后执行 
为了保证 高效 + 兼容性,Vue 采用 优雅降级策略,优先使用 微任务,如果不支持,再退到宏任务。
执行优先级
Vue 内部的实现(简化版):
- 
Promise.then(微任务,现代浏览器支持) 
- 
MutationObserver(微任务,旧版浏览器) 
- 
setImmediate(IE 专用) 
- 
setTimeout(fn, 0)(宏任务,兜底方案) 
源码思路(Vue 2.x)
let callbacks = [];
let pending = false;function flushCallbacks() {pending = false;const copies = callbacks.slice(0);callbacks.length = 0;for (let cb of copies) {cb();}
}let timerFunc;// 优先使用 Promise
if (typeof Promise !== 'undefined') {const p = Promise.resolve();timerFunc = () => p.then(flushCallbacks);
} else if (typeof MutationObserver !== 'undefined') {// 用 MutationObserver 模拟微任务let counter = 1;const observer = new MutationObserver(flushCallbacks);const textNode = document.createTextNode(String(counter));observer.observe(textNode, { characterData: true });timerFunc = () => {counter = (counter + 1) % 2;textNode.data = String(counter);};
} else {// 兜底方案timerFunc = () => setTimeout(flushCallbacks, 0);
}export function nextTick(cb, ctx) {callbacks.push(() => {if (cb) cb.call(ctx);});if (!pending) {pending = true;timerFunc();}
}
核心点
- 
利用微任务优先级:在当前事件循环的尾部、DOM 更新后执行回调 
- 
批量处理:同一事件循环内的多次 nextTick,只会触发一次真正的异步调度(性能优化) 
简单流程图
修改 data  →  Vue 标记更新 →  异步队列执行 patch →  DOM 更新完成 →  nextTick 回调执行
和 setTimeout 区别
- 
nextTick = 微任务(大多数情况下) 
- 
setTimeout = 宏任务,执行会比微任务晚一拍 
- 
所以 nextTick 比 setTimeout(..., 0) 更快 
