這篇文章帶給大家的內容是關於Vue2.6中的nextTick方法分析),有一定的參考價值,有需要的朋友可以參考一下,希望對你有幫助。
淺析 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 // issue #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 指向;
$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,如果更新是在v-on 事件處理程序中觸發的,nextTick 會優先使用macrotask。
#以上是Vue2.6中的nextTick方法分析的詳細內容。更多資訊請關注PHP中文網其他相關文章!