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

寫文章不容易,點個讚唄兄弟 專一 Vue 源碼分享,文章分爲白話版和 源碼版,白話版助於理解工做原理,源碼版助於瞭解內部詳情,讓咱們一塊兒學習吧 研究基於 Vue版本 【2.5.17】promise

若是你以爲排版難看,請點擊 下面連接 或者 拉到 下面關注公衆號也能夠吧bash

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

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

【Vue原理】NextTick - 白話版 簡單瞭解下NextTick學習

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

埋下過兩個問題this

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

二、Vue 爲何須要 宏任務 和微任務
複製代碼

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

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

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


宏微任務的使用場景

一、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 就解決了冒泡,可是這樣就不能用冒泡了好像??

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

公衆號
相關文章
相關標籤/搜索