
寫文章不容易,點個贊呗兄弟 專注 Vue 源碼分享,文章分為白話版和 源碼版,白話版助于了解工作原理,源碼版助于了解内部詳情,讓我們一起學習吧 研究基于 Vue版本 【2.5.17】
如果你覺得排版難看,請點選
下面連結或者 拉到 下面
關注公衆号也可以吧
【Vue原理】NextTick - 源碼版 之 宏微任務的抉擇
nextTick 已經寫了三篇文章啦,這是最後一篇源碼版,沒看過的童鞋可以看看白話版簡單了解下拉
【Vue原理】NextTick - 白話版 簡單了解下NextTick
在前面的文章 NextTick-源碼版之獨立自身 中
埋下過兩個問題
1、Vue 在哪裡使用到了 宏任務和 微任務
2、Vue 為什麼需要 宏任務 和微任務
今天的任務就是解決這兩個問題!!!
在這裡,大家肯定必須一定要了解了 宏任務和 微任務的哈,這兩個東西不贅述了
首先,第一個問題就是宏微任務的使用場景場景
宏微任務的使用場景
1、Vue 一般情況下使用的是微任務
2、在綁定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 就解決了冒泡,但是這樣就不能用冒泡了好像??
也不知道有沒有什麼壞處,如果有的話,後面尤大肯定會更新的