浅析 Vue 2.6 中的 nextTick 方法
栏目: JavaScript · 发布时间: 5年前
内容简介:浅析JS 的这个
浅析 Vue 2.6 中的 nextTick 方法。
事件循环
JS 的 事件循环 和 任务队列 其实是理解 nextTick 概念的关键。
这个 网上 其实有很多优质的文章做了详细介绍,我就简单过过了。
以下内容适用于浏览器端 JS,NodeJS 的事件循环机制并不相同。
规范
中规定 task 分为两大类: task(macrotask)
和 microtask
。
通常认为是 task
的任务源:
setTimeout / setInterval setImmediate MessageChannel I/O UI rendering
通常认为是 microtask
的任务源:
Promise process.nextTick MutationObserver Object.observe(已废弃)
简单概况: (这里是官方规范)
- 首先开始 执行 script 脚本 ,直到执行上下文栈为空时,开始清空 microtask 队列 里的任务,队列嘛,先入先出,出一个执行一个,清空完毕,走事件循环。
- 事件循环 :不断地去取 task 队列 的中的一个任务推入栈中执行,并在当次循环里依次清空 microtask 队列 里的任务,清空之后,可能会触发页面更新渲染(由浏览器决定)。
- 之后重复 事件循环 步骤。
nextTick
Vue 中数据的变化到 DOM 的更新渲染是一个异步过程。
此方法便用于在 DOM 更新循环结束之后执行延迟回调。
使用方法很简单:
// 修改数据 vm.msg = 'Hello'; // DOM 还没有更新 Vue.nextTick(function() { // DOM 更新了 }); // 作为一个 Promise 使用 Vue.nextTick().then(function() { // DOM 更新了 });
源码 去除注释,其实也只有不到一百来行,整体还是很容易理解的。
这里划成 3 个部分介绍。
模块变量
介绍 引入的模块 和 定义的变量。
// noop 空函数,可用作函数占位符 import { noop } from 'shared/util'; // Vue 内部的错误处理函数 import { handleError } from './error'; // 判断是IE/IOS/内置函数 import { isIE, isIOS, isNative } from './env'; // 使用 MicroTask 的标识符 export let isUsingMicroTask = false; // 以数组形式存储执行的函数 const callbacks = []; // nextTick 执行状态 let pending = false; // 遍历函数数组执行每一项函数 function flushCallbacks() { pending = false; const copies = callbacks.slice(0); callbacks.length = 0; for (let i = 0; i < copies.length; i++) { copies[i](); } }
异步延迟函数
接下来是核心的 异步延迟函数 。这里不同的 Vue 版本采用的策略其实并不相同。
2.6版本优先使用 microtask 作为异步延迟包装器。
2.5版本则是 macrotask 结合 microtask 。然而,在重绘之前状态改变时会有小问题(如 #6813 )。此外,在事件处理程序中使用 macrotask 会导致一些无法规避的奇怪行为(如 #7109 , #7153 , #7546 , #7834 , #8109 )。
所以 2.6 版本现在又改用 microtask 了,为什么是又呢。。因为 2.4 版本及之前也是用的 microtask 。。。
microtask在某些情况下也是会有问题的,因为 microtask 优先级比较高,事件会在顺序事件(如 #4521 , #6690 有变通方法)之间甚至在同一事件的冒泡过程中触发( #6566 )。
// 核心的异步延迟函数,用于异步延迟调用 flushCallbacks 函数 let timerFunc; // timerFunc 优先使用原生 Promise // 原本 MutationObserver 支持更广,但在 iOS >= 9.3.3 的 UIWebView 中,触摸事件处理程序中触发会产生严重错误 if (typeof Promise !== 'undefined' && isNative(Promise)) { const p = Promise.resolve(); timerFunc = () => { p.then(flushCallbacks); // IOS 的 UIWebView,Promise.then 回调被推入 microtask 队列但是队列可能不会如期执行。 // 因此,添加一个空计时器“强制”执行 microtask 队列。 if (isIOS) setTimeout(noop); }; isUsingMicroTask = true; // 当原生 Promise 不可用时,timerFunc 使用原生 MutationObserver // 如 PhantomJS,iOS7,Android 4.4 // #6466 MutationObserver 在 IE11 并不可靠,所以这里排除了 IE } else if ( !isIE && typeof MutationObserver !== 'undefined' && (isNative(MutationObserver) || // PhantomJS 和 iOS 7.x MutationObserver.toString() === '[object MutationObserverConstructor]') ) { 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); }; isUsingMicroTask = true; // 如果原生 setImmediate 可用,timerFunc 使用原生 setImmediate } else if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) { timerFunc = () => { setImmediate(flushCallbacks); }; } else { // 最后的倔强,timerFunc 使用 setTimeout timerFunc = () => { setTimeout(flushCallbacks, 0); }; }
一句话总结优先级: microtask 优先 。
Promise> MutationObserver > setImmediate > setTimeout
nextTick 函数
nextTick 函数。接受两个参数:
- cb 回调函数 :是要延迟执行的函数;
- ctx :指定 cb 回调函数 的 this 指向 ;
Vue 实例方法 $nextTick 做了进一步封装,把 ctx 设置为 当前 Vue 实例 。
export function nextTick(cb?: Function, ctx?: Object) { let _resolve; // 调用函数,cb 回调函数会经统一处理压入 callbacks 数组 callbacks.push(() => { if (cb) { // 给 cb 回调函数执行加上了 try-catch 错误处理 try { cb.call(ctx); } catch (e) { handleError(e, ctx, 'nextTick'); } } else if (_resolve) { _resolve(ctx); } }); // 执行异步延迟函数 timerFunc if (!pending) { pending = true; timerFunc(); } // 当 nextTick 没有传入函数参数的时候,返回一个 Promise 化的调用 if (!cb && typeof Promise !== 'undefined') { return new Promise(resolve => { _resolve = resolve; }); } }
小结
整体看过来,感觉还是比较好理解的吧~ 2.6 版本相比之前简化了一点。
小结一下,每次调用 Vue.nextTick(cb)
会做些什么:
cb 函数经处理压入 callbacks 数组 ,执行 timerFunc 函数 ,延迟调用 flushCallbacks 函数 ,遍历执行 callbacks 数组 中的所有函数。
延迟调用优先级如下:
Promise> MutationObserver > setImmediate > setTimeout
版本差异
其实 Vue 2.4、2.5、2.6 版本的 nextTick 都略不一样。
整体 2.6 和 2.4 的比较相似。(仔细瞅了瞅,基本就是一样的,2.6 timerFunc 多了个 setImmediate 判断)
2.5版本其实也差不多。。。源码写法有些不一样,整体优先级是: Promise > setImmediate > MessageChannel > setTimeout 。感兴趣可自行去末尾的参考地址进行查阅。
参考:
以上所述就是小编给大家介绍的《浅析 Vue 2.6 中的 nextTick 方法》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- 浅析 Docker 容器安全管控方法
- 浅析Docker容器安全管控方法
- 洗牌算法及 random 中 shuffle 方法和 sample 方法浅析
- 浅析前端加密后数据包的修改方法
- 浅析“远程对象调用”
- 浅析“远程对象调用”
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。