Vue是如何实现nextTick的?

news/2024/12/23 2:11:53/

你好同学,我是沐爸,欢迎点赞、收藏和关注。个人知乎

Vue.js 的 nextTick 函数是一个非常重要的功能,它用于延迟执行代码块到下次 DOM 更新循环之后。这在 Vue.js 的异步更新队列机制中非常有用,尤其是在你需要基于更新后的 DOM 来执行某些操作时。

实现原理

Vue.js 的 nextTick 的实现原理主要依赖于 JavaScript 的事件循环和微任务(microtask)或宏任务(macrotask)队列。Vue 使用了一种叫做“异步更新队列”的技术来优化 DOM 的更新。当你修改 Vue 组件的数据时,Vue 不会立即更新 DOM,而是将这些更新任务放入一个队列中,并在同一个事件循环的“下次迭代”中执行它们。这样做的好处是可以批量处理多次数据变化,只进行一次 DOM 更新,从而提高性能。

nextTick 函数允许你指定一个回调函数,这个回调函数会在 DOM 更新完成后被调用。Vue2和Vue3的实现有所不同。

Vue2的实现

Vue2通过判断浏览器环境,选择最适合当前环境的异步任务执行方式,以确保回调函数能够尽快执行,从而提高应用的响应速度和性能。

  1. Promise(如果浏览器支持)
    Vue 会检查是否支持 Promise,并尝试使用 Promise.resolve().then() 来安排回调函数在微任务队列中执行。这是因为微任务会在当前执行栈清空后立即执行,但在宏任务(如 setTimeout)之前。
  2. MutationObserver
    如果 Promise 不可用,Vue 会尝试使用 MutationObserver。MutationObserver API 用于在 DOM 树发生变化时异步执行回调函数。Vue 会创建一个文本节点作为观察目标,并在回调函数中调用你的 nextTick 回调函数。然后,它会改变这个文本节点的某些内容来触发 MutationObserver 的回调。
  3. setImmediate
    如果以上两者都不可用,Vue 会尝试使用 setImmediate(这是一个比 setTimeout 更快的 API,但在大多数环境中不可用)。
  4. setTimeout
    作为最后的回退选项,Vue 会使用 setTimeout 来安排回调函数在宏任务队列中执行。这是所有选项中最慢的,因为它会在浏览器重绘和重排之后执行。

源码:

var timerFunc; // 定时器函数
if (typeof Promise !== 'undefined' && isNative(Promise)) {var p_1 = Promise.resolve();timerFunc = function () { // 1.使用Promise.then()p_1.then(flushCallbacks);if (isIOS)setTimeout(noop);};isUsingMicroTask = true;
} else if (!isIE &&typeof MutationObserver !== 'undefined' &&(isNative(MutationObserver) ||MutationObserver.toString() === '[object MutationObserverConstructor]')) {var counter_1 = 1;var observer = new MutationObserver(flushCallbacks);var textNode_1 = document.createTextNode(String(counter_1));observer.observe(textNode_1, {characterData: true});timerFunc = function () { // 2.使用MutationObservercounter_1 = (counter_1 + 1) % 2;textNode_1.data = String(counter_1);};isUsingMicroTask = true;
} else if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) {timerFunc = function () { // 3.使用setImmediatesetImmediate(flushCallbacks);};
} else {timerFunc = function () { // 4.使用setTimeoutsetTimeout(flushCallbacks, 0);};
}function nextTick(cb, ctx) {var _resolve;callbacks.push(function () {if (cb) {try {cb.call(ctx);}catch (e) {handleError(e, ctx, 'nextTick');}}else if (_resolve) {_resolve(ctx);}});if (!pending) {pending = true;timerFunc(); // 执行定时器}// $flow-disable-lineif (!cb && typeof Promise !== 'undefined') {return new Promise(function (resolve) {_resolve = resolve;});}
}

Vue3的实现

不知道是因为Promise的支持度高,还是Vue3不在兼容低版本的浏览器,Vue3直接使用了Promise.then()这个微任务来处理DOM更新后的回调。

源码:

const resolvedPromise = /* @__PURE__ */ Promise.resolve();
let currentFlushPromise = null;
function queueFlush() {if (!isFlushing && !isFlushPending) {isFlushPending = true;currentFlushPromise = resolvedPromise.then(flushJobs);}
}function nextTick(fn) {const p = currentFlushPromise || resolvedPromise;return fn ? p.then(this ? fn.bind(this) : fn) : p;
}

查看源码可知,Vue3没有再向Vue2那样判断浏览器环境来选择异步任务执行方式,而是直接采用了Promise.then()方法。

希望对你有所帮助,下期再见!


http://www.ppmy.cn/news/1510632.html

相关文章

npm install

文章目录 npm install安装vue npm install 使用国内的镜像源来进行类似npm install的操作,主要目的是提高依赖包的下载速度,因为npm的默认源位于国外,对于国内用户来说下载速度可能较慢。以下是一些具体步骤,以使用淘宝的npm镜像…

动手学大模型应用全栈开发 #Datawhale AI 夏令营

文章目录 🚩baseline💡Demo搭建!💡启动Demo💡转战飞桨🔈启动Demo🔈torch 改 paddle 💡二周目魔搭🔈贴一份CPU版代码 🚩baseline 💡Demo搭建&…

AWS域名注册服务:为您的在线业务打下坚实基础

在如今的数字时代,域名是每个在线业务的基础。一个好的域名不仅可以提升品牌形象,还能为用户提供便捷的访问体验。亚马逊网络服务(AWS)提供了强大的域名注册服务,帮助企业轻松获取和管理域名。我们九河云将深入探讨AWS…

【iOS】—— Runloop和多线程问题总结

Runloop和多线程问题总结 runloop总结1. runloop简介2. runloop的基本作用3. 获取runloop的流程4. runloop和线程的关系5. runloop中的Mode有几种以及作用6.runloop的事件源7. 讲一下source0和source18. runloop的六种观察者模式9. 针对定时器在滑动时停止工作的问题10. 如何解…

66_1JSON【浏览器中处理JSON、Java中处理JSON(FastJSON、Jackson)】、Java中的bean

JSON 概念 JSON:JavaScript Object Notation是一种表示对象的方式 基于JavaScript语言的轻量级的数据交换格式;(即:用来传输数据的一种格式) 现在传输数据的方式更多是采用json的格式,渐渐代替了XML JSON的数据表示 JSON采用名值…

Element UI中el-dialog作为子组件如何由父组件控制显示/隐藏~

1、这里介绍的是将el-dialog作为组件封装便于复用,如何通过父组件控制子组件dialog的显示与隐藏。 2、思路:首先el-dialog是通过dialogVisible的值是否为true或false来控制显示与隐藏的。那么我们可以通过父传子props来将true(即showFlag的值&#xff0…

squeeze()和unsequeeze()函数的作用

sequeeze(dim):用于在指定位置添加一个大小为1的新维度 例如: 其他的大家自行尝试! squeeze():squeeze函数用于去除张量中所有大小为1的维度,注意当没有传入任何参数的时候,去掉的是所有大小为1的维度。 例如:

【leetcode】相交链表-25-1

方法:遍历 /*** Definition for singly-linked list.* struct ListNode {* int val;* ListNode *next;* ListNode(int x) : val(x), next(NULL) {}* };*/ class Solution { public:ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {…