Vue 的 nextTick 的原理是什么?

Vue.nextTick 的核心作用是 在 DOM 更新完成后执行回调,它的原理和事件循环机制(Event Loop) 以及 微任务(Microtask) 紧密相关。

为什么需要 nextTick?

Vue 的数据更新是异步的,也就是:

  • 当你修改 data 时,Vue 并不会立刻更新 DOM

  • 它会将更新操作 推入一个队列,在 同一事件循环中合并多次修改,最后一次性更新 DOM(提升性能)

所以:

  • 如果你想在修改数据后,拿到最新的 DOM,就要等更新完成

  • 这就是 Vue.nextTick 的作用

nextTick 的原理

nextTick 的核心逻辑:

  1. 把回调放进一个任务队列中

  2. 选择一个合适的时机,在 DOM 更新完成后执行

为了保证 高效 + 兼容性,Vue 采用 优雅降级策略,优先使用 微任务,如果不支持,再退到宏任务。

执行优先级

Vue 内部的实现(简化版):

  1. Promise.then(微任务,现代浏览器支持)

  2. MutationObserver(微任务,旧版浏览器)

  3. setImmediate(IE 专用)

  4. 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) 更快

posted @ 2025-08-13 17:05  煜火  阅读(28)  评论(0)    收藏  举报