【Vue原理】NextTick - 源碼版 之 宏微任務的抉擇

專一 Vue 源碼分享,文章分爲白話版和 源碼版,白話版助於理解工做原理,源碼版助於瞭解內部詳情,讓咱們一塊兒學習吧vue

研究基於 Vue版本2.5.17promise


nextTick 已經寫了三篇文章啦,這是最後一篇源碼版,沒看過的童鞋能夠看看白話版簡單瞭解下拉
微信

NextTick - 白話版
app

在前面的文章 NextTick - 源碼版 之 獨立自身異步

埋下過兩個問題編輯器

一、Vue 在哪裏使用到了 宏任務和 微任務學習

二、Vue 爲何須要 宏任務 和微任務flex

今天的任務就是解決這兩個問題!!!ui

在這裏,你們確定必須必定要了解了 宏任務和 微任務的哈,這兩個東西不贅述了this

首先,第一個問題就是宏微任務的使用場景場景

插播新聞

前幾天有個童鞋後臺問了我個問題,由於時間過久,沒法直接回復,因此只好在這裏回覆了

數據 setter 以後觸發的 vm._update 不是整個vue實例的 vm,而是 組件的 vm。我這裏假設的場景是

在父組件中,子組件A有一個數據 name,而且子組件A引用了 name ,那麼這個name 只會收集到 子組件A 的vm,當 name 的 setter 被觸發,那麼就只會更新這個組件(這就是Vue組件如何精確更新的緣由)

其實這部份內容在依賴收集中也講過 ,能夠看下 依賴收集 - 源碼版之基本數據類型

就是數據被哪一個實例引用了,那麼就只會收集了誰的實例


好的廢話說完了,轉到正題來!

第一個問題就是宏微任務的使用場景場景


宏微任務的使用場景

一、Vue 通常狀況下使用的是微任務

二、在綁定DOM 事件的時候,會使用宏任務。

這麼講,有點籠統,準確地說,應該是

事件回調執行過程當中,在JS 主線程爲空以後,異步代碼執行以前,全部經過 nextTick 註冊的異步代碼都是用宏任務。

來看看綁定DOM 事件的源碼

經過 addEventListener 給 DOM 綁定事件


function add$1(event, handler) {
   handler = withMacroTask(handler);
   target$1.addEventListener(event, handler);}function withMacroTask(fn) {        return fn._withTask || (fn._withTask = function() {       useMacroTask = true;                var res = fn.apply(null, arguments);       useMacroTask = false;                return res   })
}

你看到了,把原先DOM 事件的回調包裝了一遍,而後經過設置 useMacroTask 來控制註冊宏任務

useMacroTask 沒見過,在 nextTick 獨立流程中已經講過了的

在調用 nextTick 的時候,正是經過這個變量來控制,這次異步代碼註冊的任務類型


Vue.nextTick =function (cb, ctx) {
   callbacks.push(function() {
       cb && cb.call(ctx);   });        if (!pending) {       pending = true;                if (useMacroTask) {           macroTimerFunc();
       } else {
           microTimerFunc();
       }
   }
}


好多,如今咱們來解決第二個問題!


爲何須要宏微任務

爲何要特意在事件回調執行期間 使用宏任務啊,想了好很久啊,才腦抽想到去看了下 Vue 的註釋

大概意思是這樣

原本 Vue 是歷來都使用微任務的,由於微任務的優先級比較高,執行比較快。可是同時也是由於這樣致使了一個問題

什麼問題?

在連續事件發生的期間,微任務就已經執行了

就是

事件回調執行完成以後,會立刻執行微任務

那麼連續多個事件回調同時執行,就會致使連續屢次執行微任務

若是連續多個事件回調中,都有修改數據,以下

this.state = xxxxx

那麼很明顯,會致使頁面頻繁的更新,這顯然不是咱們想要的結果

那到底什麼是連續的事件?

那就是冒泡!

咱們來現場演示一下微任務下的冒泡事件

<div style="height:100px;width:100px;background:red">
 <div style="height:60px;width:60x;background:black">
   <div style="height:30px;width:30px;background:blue">
   </div> </div></div>

div1.onclick = function() {        console.log("div1");   Promise.resolve().then(() = >{                console.log("promise1")   })
}div2.onclick = function() {        console.log("div2");        Promise.resolve().then(() = >{                console.log("promise2")   })
}div3.onclick = function() {        console.log("div3");        Promise.resolve().then(() = >{                console.log("promise3")   })
}


看到了嗎,promise 在一個事件回調結束以後立刻就調用了

若是在 Vue 中的事件回調中修改了數據 this.state = xxxxx

而後數據一更改,就會註冊微任務用於響應更新,而後事件結束以後,立刻執行微任務

若是三個事件回調都有修改數據,那麼就會註冊三次,執行三次,就會更新三次

因此

尤大想到了一個方法,就是在事件回調執行時,註冊的是宏任務

宏任務並不會在事件結束以後立刻調用

只會在連續事件結束以後,才調用,這就是咱們想要的

因此你才能看到 使用 useMacroTask 來控制註冊的任務類型

如今我把上面的例子中的 promise 換成 setTimeout,從新點擊一下

可是!!

在 【Vue 2.6】 中,咱們已經看不到 useMacroTask 的身影了,爲何?

由於 Vue 又所有使用微任務了........ 天道輪迴.....

(其實並非所有是微任務,兼容寫法最後是 setTimeout)

你問,那冒泡又怎麼辦?

好吧,尤大想到了另外一個辦法來解決冒泡的問題

就是判斷當時的 事件 target,來判斷是否執行事件回調

也就間接解決了這個問題,看看新的綁定事件的源碼

function add$1(name, handler) {   handler = function(e) {                if (            e.target === e.currentTarget ||            e.target.ownerDocument !== document        ) {           return  handler.apply(this, arguments)       }
   };
   target$1.addEventListener(name, handler);
}

經過判斷 target 就解決了冒泡,可是這樣就不能用冒泡了好像??

也不知道有沒有什麼壞處,若是有的話,後面尤大確定會更新的


最後

鑑於本人能力有限,不免會有疏漏錯誤的地方,請你們多多包涵,若是有任何描述不當的地方,歡迎後臺聯繫本人,領取紅包

本文分享自微信公衆號 - 神仙朱(skying-zhu)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。

相關文章
相關標籤/搜索