Vue 的 nextTick 的原理是什么?
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) 更快
浙公网安备 33010602011771号