自学内容网 自学内容网

$nextTick 实现原理

Vue 使用 nextTick 来确保数据更新后的 DOM 操作在更新完成后执行。其核心逻辑是将回调放到微任务或宏任务队列中,确保回调在 DOM 更新完成后执行。

Vue.js 会利用不同的浏览器 API 来模拟 nextTick 的延迟执行,通常是通过:

  1. Promise:在微任务队列中执行。
  2. setTimeout:在宏任务队列中执行(当浏览器不支持 Promise 时)。

Vue.js 的 nextTick 会将回调函数放到合适的队列中执行。通过不同的 异步队列机制 来确保在 DOM 更新后执行回调函数。实现方式如下:

function nextTick(callback) {
  if (Promise) {
    Promise.resolve().then(callback);  // 使用 Promise 微任务队列
  } else if (setImmediate) {
    setImmediate(callback);  // 支持 setImmediate
  } else {
    setTimeout(callback, 0);  // 最终回退到 setTimeout
  }
}

实现流程:

  1. 回调队列:callbacks 用于存储所有通过 nextTick 添加的回调函数。
  2. 防止重复调用:pending 用于标记当前是否已经在等待刷新回调,避免重复调用。
  3. 任务优先级选择:Promise 微任务 > MutationObserver>setTimeout(宏任务)
  4. 执行回调:flushCallbacks 会执行所有的回调函数,并清空队列。
let callbacks = [];      // 存储回调函数的数组,用于收集 nextTick 的回调
let pending = false;     // 标记当前是否已经在等待刷新回调,防止多次触发

function flushCallbacks() {
  pending = false;       // 重置标记,表示已经进入回调执行阶段
  // 复制回调数组,防止在执行回调时继续往 callbacks 里添加新的回调
  const copies = callbacks.slice(0);
  callbacks.length = 0;  // 清空原始回调数组
  // 逐个执行回调函数
  for (let i = 0; i < copies.length; i++) {
    copies[i]();
  }
}

let timerFunc;

// 优先使用 Promise 微任务,其次是 MutationObserver,最后是 setTimeout
if (typeof Promise !== 'undefined') {
  const p = Promise.resolve();     // 创建一个 resolved 状态的 Promise 实例
  timerFunc = () => {
    p.then(flushCallbacks);        // 使用 Promise.then 触发微任务队列中的 flushCallbacks
  };
} else if (typeof MutationObserver !== 'undefined') {
  let counter = 1;
  const observer = new MutationObserver(flushCallbacks);  // 使用 MutationObserver 监听数据变化触发 flushCallbacks
  const textNode = document.createTextNode(String(counter)); // 创建文本节点作为观察对象
  observer.observe(textNode, { characterData: true });    // 监听文本节点的数据变化
  timerFunc = () => {
    counter = (counter + 1) % 2;  // 切换 counter 值,触发文本节点变化
    textNode.data = String(counter); // 触发 MutationObserver 回调
  };
} else {
  timerFunc = () => {
    setTimeout(flushCallbacks, 0); // 最后降级到使用 setTimeout 延迟执行 flushCallbacks
  };
}

function nextTick(cb) {
  callbacks.push(cb);    // 将回调函数存入 callbacks 数组中
  if (!pending) {        // 如果没有待处理的回调任务
    pending = true;      // 设置标记,防止重复触发
    timerFunc();         // 调用 timerFunc,触发回调执行机制
  }
}
  • callbacks 存储所有传入的回调函数。

  • pending 标记防止 timerFunc 多次调用,确保只在本次任务队列完成后触发。

  • flushCallbacks 函数会在 timerFunc 被触发时执行,清空原数组 callbacks 并执行所有回调函数。

  • timerFunc 的优先级选择:

    • Promise:优先使用 Promise 的 then 方法,将 flushCallbacks 放入微任务队列,微任务优先于宏任务。
    • MutationObserver:在不支持 Promise 时,使用 MutationObserver 来监听文本节点变化。每次 timerFunc 被调用,都会修改文本节点内容,触发 MutationObserver 回调。
    • setTimeout:如果以上两种都不支持,使用 setTimeout(宏任务)延迟执行。
  • nextTick 将传入的回调 cb 存入 callbacks 队列,并确保 flushCallbacks 仅被触发一次。


原文地址:https://blog.csdn.net/qq_36437172/article/details/143580597

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!