摘要:倡導(dǎo)開發(fā)者盡量不直接操作,但有的時候由于各種需求讓開發(fā)者不得不這樣做,于是的實(shí)現(xiàn)就是讓開發(fā)者在修改數(shù)據(jù)后,能夠在數(shù)據(jù)更新到后才執(zhí)行對應(yīng)的函數(shù),從而獲取最新的數(shù)據(jù)。
Vue 倡導(dǎo)開發(fā)者盡量不直接操作 DOM,但有的時候由于各種需求讓開發(fā)者不得不這樣做,于是 nextTick 的實(shí)現(xiàn)就是讓開發(fā)者在修改數(shù)據(jù)后,能夠在數(shù)據(jù)更新到 DOM 后才執(zhí)行對應(yīng)的函數(shù),從而獲取最新的 DON 數(shù)據(jù)。
原文地址
項(xiàng)目地址
那么如何實(shí)現(xiàn) nextTick呢,我們首先可以想到的是利用 setTimeout 的異步回調(diào)來實(shí)現(xiàn),不過由于各個瀏覽器的不同,setTimeout 的延遲很高,因此在 nextTick 中只作為最后的備胎,首選的方案則是 MutationObserver(在后面的內(nèi)容中 MO 代表 MutationObserver)
nextTick 的源碼實(shí)現(xiàn)export const nextTick = (function () { var callbacks = [] var pending = false var timerFunc function nextTickHandler () { pending = false var copies = callbacks.slice(0) callbacks = [] for (var i = 0; i < copies.length; i++) { copies[i]() } } /* istanbul ignore if */ if (typeof MutationObserver !== "undefined") { // 首選 MutationObserver var counter = 1 var observer = new MutationObserver(nextTickHandler) // 聲明 MO 和回調(diào)函數(shù) var textNode = document.createTextNode(counter) observer.observe(textNode, { // 監(jiān)聽 textNode 這個文本節(jié)點(diǎn) characterData: true // 一旦文本改變則觸發(fā)回調(diào)函數(shù) nextTickHandler }) timerFunc = function () { counter = (counter + 1) % 2 // 每次執(zhí)行 timeFunc 都會讓文本在 1 和 0 間切換 textNode.data = counter } } else { timerFunc = setTimeout // 如果不支持 MutationObserver, 退選 setTimeout } return function (cb, ctx) { var func = ctx ? function () { cb.call(ctx) } : cb callbacks.push(func) if (pending) return pending = true timerFunc(nextTickHandler, 0) } })()MutationObserver 的功能和作用
MO 給開發(fā)者提供了一種能在某個范圍內(nèi)的DOM數(shù)發(fā)生變化時作出適當(dāng)反應(yīng)的能力 ——MDN
用人話說是開發(fā)者能通過它創(chuàng)建一個觀察者對象,這個對象會監(jiān)聽某個DOM元素,并在它的DOM樹發(fā)生變化時執(zhí)行我們提供的回調(diào)函數(shù)。
具體參考這個 DEMO
比較特別的是實(shí)例化的時候需要先傳入回調(diào)函數(shù):
var observer = new MutationObserver(function(mutations) { mutations.forEach(function(mutation) { console.log(mutation.type); }) })
然后才配置觀察選項(xiàng),包括觀察節(jié)點(diǎn)和觀察的屬性:
// 選擇目標(biāo)節(jié)點(diǎn) var target = document.querySelector("#some-id"); // 配置觀察選項(xiàng): var config = { attributes: true, childList: true, characterData: true } // 傳入目標(biāo)節(jié)點(diǎn)和觀察選項(xiàng) observer.observe(target, config); // 隨后,你還可以停止觀察 observer.disconnect();
對于老版本的谷歌和火狐,則需要使用帶前綴的 MO:
var MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserverMutationObserver 和 microtask
那么為什么優(yōu)選使用 MutationObserver呢?
一開始以為是 MO 就是用來監(jiān)聽 DOM 變化的,那么使用 textnode 模擬 DOM 變化再利用 MO 來監(jiān)聽觸發(fā)從而實(shí)現(xiàn) nextTick 不就很適合,直到了解看到了知乎上的問答才知道是因?yàn)?MO 會比 setTimeout 早執(zhí)行的緣故,
這里需要了解JS的運(yùn)行運(yùn)行機(jī)制(重新刷新了我的三觀), JS 的事件運(yùn)行機(jī)制執(zhí)行的時候會區(qū)分 task 和 microtask, 引擎在每個 task 執(zhí)行完畢,并在從隊列里取下一個task來執(zhí)行之前, 執(zhí)行完所有的 microtask 隊列中的 microtask
(task 和 microtask 摘自 https://jakearchibald.com/)
setTimeout 回調(diào)會被分配到一個新的task中等待執(zhí)行,而 Promise 的 resolver、MO 的 回調(diào)都會被分配到 microtask 的隊列中,所以會比 setTimout 先執(zhí)行
除了比 setTimout 快之外,還有 渲染性能 的問題,根據(jù)HTML Standard, 每個 task 運(yùn)行完以后, UI 都會重新渲染,那么在 microtask 中就完成數(shù)據(jù)更新, 當(dāng)前 task 結(jié)束就可以得到最新的 UI, 反之如果新建一個 task 來做數(shù)據(jù)更新,那么渲染就會進(jìn)行兩次。
所以性價比如此高的 MO 自然成為了首選
關(guān)于 microtask,具體可以閱讀 Jake 寫的 Tasks, microtasks, queues and schedules
nextTick 的版本迭代上面關(guān)于 nextTick 的源碼實(shí)現(xiàn)屬于 vue 最早的版本 v1.0.9,在深挖 mutationObserver 的時候發(fā)現(xiàn) nextTick 在vue的版本迭代中也在不斷的進(jìn)化,同事也發(fā)生過退化,非常有趣:
先說說退化的事件,尤大(vue的作者)曾經(jīng)使用 window.postMessage 來替代 MO 實(shí)現(xiàn) nextTick,結(jié)果開發(fā)者使用后發(fā)現(xiàn)了問題,可以看看這兩個 JSFiddle:jsfiddle1 和 jsfiddle2, 兩個例子用了不同版本來實(shí)現(xiàn)元素的絕對定位,第一個使用的是 2.0.0-rc6,這個版本采用的是 MO,而后來因?yàn)?IOS 9.3 的 WebView 里 MO 有 bug,尤大便換成 window.postMessage來實(shí)現(xiàn),即第二個實(shí)例版本為 2.0.0-rc7, 但是由于 postMessage 會將回調(diào)放到 macrotask 其實(shí)也就是 task 里面,導(dǎo)致可能執(zhí)行了多次 UI 的task都沒有執(zhí)行 window.postMessage 的 task,也就延遲了更新DOM操作的時間。尤大在后續(xù)版本撤回了這一次修改,具體的討論可以看issue
關(guān)于進(jìn)化,在后續(xù)的版本里,由于 es6 的新語法,nextTick 開始使用 Promise.then 和 MO 來做首選和次選,在前面的討論中已經(jīng)提到,Promise.then 也屬于 microtask。
資源MutationObserver MDN
Tasks, microtasks, queues and schedules
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://www.ezyhdfw.cn/yun/88447.html
摘要:原文地址項(xiàng)目地址關(guān)于中使用效果,官網(wǎng)上的解釋如下當(dāng)元素插入到樹或者從樹中移除的時候,屬性提供變換的效果,可以使用來定義變化效果,也可以使用來定義首先第一個函數(shù)是將元素插入,函數(shù)實(shí)現(xiàn)調(diào)用了實(shí)現(xiàn)代碼如下寫的好的代碼就是文檔,從注釋和命名上就 src/transition 原文地址項(xiàng)目地址 關(guān)于 vue 中使用 transition 效果,官網(wǎng)上的解釋如下: With Vue.js’ tra...
摘要:它被當(dāng)做一個輕量版本的使用,用于存儲已排好版的或尚未打理好格式的片段。最大的區(qū)別是因?yàn)椴皇钦鎸?shí)樹的其中一部分,它的變化不會引起樹的重新渲染的操作,或者導(dǎo)致性能影響的問題出現(xiàn)。 原文地址項(xiàng)目地址 工具類 /** * Simple bind, faster than native * * @param {Function} fn * @param {Object} ctx * @...
摘要:我們發(fā)現(xiàn)默認(rèn)是使用異步執(zhí)行更新。優(yōu)先使用,在不存在的情況下使用,這兩個方法的回調(diào)函數(shù)都會在中執(zhí)行,它們會比更早執(zhí)行,所以優(yōu)先使用。是最后的一種備選方案,它會將回調(diào)函數(shù)加入中,等到執(zhí)行。 寫在前面 因?yàn)閷ue.js很感興趣,而且平時工作的技術(shù)棧也是Vue.js,這幾個月花了些時間研究學(xué)習(xí)了一下Vue.js源碼,并做了總結(jié)與輸出。文章的原地址:https://github.com/ans...
摘要:而中的回調(diào)函數(shù)則會在頁面渲染后才執(zhí)行。還使用方法復(fù)制數(shù)組并把數(shù)組清空,這里的數(shù)組就是存放主線程執(zhí)行過程中的函數(shù)所傳的回調(diào)函數(shù)集合主線程可能會多次使用方法。到這里就已經(jīng)實(shí)現(xiàn)了根據(jù)環(huán)境選擇異步方法,并在異步方法中依次調(diào)用傳入方法的回調(diào)函數(shù)。 Vue.nextTick的應(yīng)用場景 Vue 是采用異步的方式執(zhí)行 DOM 更新。只要觀察到數(shù)據(jù)變化,Vue 將開啟一個隊列,并緩沖同一事件循環(huán)中發(fā)生的...
摘要:后來尤雨溪了解到是將回調(diào)放入的隊列。而且瀏覽器內(nèi)部為了更快的響應(yīng)用戶,內(nèi)部可能是有多個的而的的優(yōu)先級可能更高,因此對于尤雨溪采用的,甚至可能已經(jīng)多次執(zhí)行了的,都沒有執(zhí)行的,也就導(dǎo)致了我們更新操 原發(fā)于我的博客。 前一篇文章已經(jīng)詳細(xì)記述了Vue的核心執(zhí)行過程。相當(dāng)于已經(jīng)搞定了主線劇情。后續(xù)的文章都會對其中沒有介紹的細(xì)節(jié)進(jìn)行展開。 現(xiàn)在我們就來講講其他支線任務(wù):nextTick和micro...
閱讀 4229·2021-11-18 13:22
閱讀 1941·2021-11-17 09:33
閱讀 2963·2021-09-26 09:46
閱讀 1297·2021-08-21 14:11
閱讀 2974·2019-08-30 15:53
閱讀 2789·2019-08-30 15:52
閱讀 2180·2019-08-30 10:52
閱讀 1604·2019-08-29 15:30