$nextTick 实现原理
Vue 使用 nextTick 来确保数据更新后的 DOM 操作在更新完成后执行。其核心逻辑是将回调放到微任务或宏任务队列中,确保回调在 DOM 更新完成后执行。
Vue.js 会利用不同的浏览器 API 来模拟 nextTick 的延迟执行,通常是通过:
- Promise:在微任务队列中执行。
- 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
}
}
实现流程:
- 回调队列:callbacks 用于存储所有通过 nextTick 添加的回调函数。
- 防止重复调用:pending 用于标记当前是否已经在等待刷新回调,避免重复调用。
- 任务优先级选择:Promise 微任务 > MutationObserver>setTimeout(宏任务)
- 执行回调: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)!