摘要:盡量把所有異步代碼放在一個宏微任務(wù)中,減少消耗加快異步代碼的執(zhí)行。我們知道,如果一個異步代碼就注冊一個宏微任務(wù)的話,那么執(zhí)行完全部異步代碼肯定慢很多避免頻繁地更新。中就算我們一次性修改多次數(shù)據(jù),頁面還是只會更新一次。
寫文章不容易,點個贊唄兄弟
專注 Vue 源碼分享,文章分為白話版和 源碼版,白話版助于理解工作原理,源碼版助于了解內(nèi)部詳情,讓我們一起學習吧
研究基于 Vue版本 【2.5.17】
如果你覺得排版難看,請點擊 下面鏈接 或者 拉到 下面關(guān)注公眾號也可以吧
【Vue原理】NextTick - 源碼版 之 獨立自身
好的,今天到了 nextTick 的環(huán)節(jié),之前我看的版本是 2.5.17,然后瞄了一眼 2.6 的,發(fā)現(xiàn)對于 nextTick 修改了 少部分內(nèi)容,但是不太大,所以就一起記錄下來
(如果改太多,就懶得看了.....反正了解一個思想以及實現(xiàn)思路就行了)
nextTick 是一個在 Vue 中比較獨立的東西,可以直接拿出來為你的項目服務(wù)
nextTick 涉及的點,就下面這些
1、任務(wù)隊列callbacks 2、任務(wù)隊列執(zhí)行函數(shù) flushCallbacks 3、控制(宏任務(wù),微任務(wù))注冊標志位 pending 4、宏任務(wù),微任務(wù)
沒看懂?沒關(guān)系,后面會慢慢說
這篇先講 nextTick 自身,下篇再講 nextTick 和 Vue 的關(guān)聯(lián)
接下來就是一個個去詳細記錄了
宏任務(wù),微任務(wù)這個知識點,很重要,也不算太簡單,在網(wǎng)上也能找到很多很好的講解,比如下面這篇文章,在這里不會特別解釋這兩個,畢竟主題不是這個
https://juejin.im/post/59e85e...
宏微任務(wù)的下面總結(jié)也是個人理解,有錯盡管罵我
那么這里就先記錄一下相關(guān)的結(jié)論
1、宏任務(wù)和微任務(wù)都是異步 2、宏任務(wù)和微任務(wù)會被注冊到兩個不同的隊列中 3、宏任務(wù)隊列不是一次性清空執(zhí)行,而是執(zhí)行一個宏任務(wù)時, 然后去清空執(zhí)行一列微任務(wù)隊列
接著再執(zhí)行下一個宏任務(wù).....循環(huán)往復,直到所有隊列都為空
什么是一個宏任務(wù)比如 一個 setTimeout 就是一個宏任務(wù),兩個 setTimeout 就是兩個宏任務(wù)
例子說明執(zhí)行順序比如現(xiàn)在,宏任務(wù)隊列中有兩個 setTimeout,微任務(wù)隊列中有兩個 Promise
假設(shè)現(xiàn)在正在執(zhí)行第一個宏任務(wù) setTimeout,執(zhí)行完之后,會開始清空執(zhí)行 微任務(wù)隊列
于是開始執(zhí)行了兩個Promise
結(jié)束之后,接著執(zhí)行 另一個宏任務(wù), setTimeout
以前我以為是 宏任務(wù)隊列執(zhí)行完,再執(zhí)行微任務(wù)隊列,發(fā)現(xiàn)不是,很受傷,都是了解 nextTick 源碼讓我有機會重新了解了一遍 這個知識點
常見宏任務(wù)setTimeout
setInterval
setImmediate
script
MessageChannel
常見微任務(wù)Promise
MutationObserver
Object.observe(廢棄)
process.nextTick(node)
Vue 中的宏任務(wù) 和 微任務(wù) 源碼以下談的是 版本 2.5.17 的,在 2.6 中,去掉宏任務(wù)了
在這里先埋下兩個問題
1、Vue為什么需要宏任務(wù)和 微任務(wù) 2、Vue在哪里使用到了宏任務(wù)和微任務(wù)
這兩個問題會記錄在另外一篇文章
Vue 中有兩個函數(shù),macroTimerFunc 用于注冊宏任務(wù),microTimerFunc 用于注冊微任務(wù)
以適用于不同的場景,下面就是這兩個函數(shù)的源碼
1、macroTimerFuncif(如果setImmediate存在) { macroTimerFunc =function(){ setImmediate(flushCallbacks); }; } elseif(如果MessageChannel存在) { varchannel =newMessageChannel(); varport = channel.port2; channel.port1.onmessage = flushCallbacks; macroTimerFunc =function(){ port.postMessage(1); }; } else{ macroTimerFunc =function(){ setTimeout(flushCallbacks,0); }; }
沒啥好說的,最多記錄一下 MessageChannel,更多內(nèi)容就自己查啦
MessageChannel
簡單來說,MessageChannel 用于創(chuàng)建了一個通信的管道,這個管道有兩個端口
每個端口都可以通過postMessage發(fā)送數(shù)據(jù)
一個端口綁定onmessage回調(diào),從另一個端口接收傳過來的數(shù)據(jù)
不多說了,看下一個微任務(wù)
2、microTimerFuncif(如果promise存在) { varp =Promise.resolve(); microTimerFunc =function(){ p.then(flushCallbacks); }; }else{ microTimerFunc = macroTimerFunc; }
上面的宏微任務(wù) 函數(shù)都 出現(xiàn)了一個 flushCallbacks 的東西,下面會有
Vue 的任務(wù)隊列vue 自己維護了一個任務(wù)隊列去配合 宏微任務(wù)使用,目的無非是幾樣
1、減少宏微任務(wù)的注冊。盡量把所有異步代碼放在一個 宏微任務(wù)中,減少消耗
2、加快異步代碼的執(zhí)行。我們知道,如果一個異步代碼就注冊一個宏微任務(wù)的話,那么執(zhí)行完全部異步代碼肯定慢很多
3、避免頻繁地更新。Vue 中就算我們一次性修改多次數(shù)據(jù),頁面還是只會更新一次。就是因為這樣,避免多次修改數(shù)據(jù)導致的多次頻繁更新頁面,讓多次修改只用更新最后一次
下面就來說一下Vue 相關(guān)的實現(xiàn)
1、callbackscallbacks 是一個數(shù)組,用于存放各種異步函數(shù)。比如
this.$nextTick(()=>{ console.log(1111) })
就會把你設(shè)置的這個回調(diào),放到 callbacks 數(shù)組中
callbacks.push(()=>{ console.log(1111) })
既然 callbacks 是存放異步回調(diào)的,那么肯定有一個方法,是遍歷 callbacks ,然后逐個執(zhí)行其中存放的函數(shù)
沒錯,這個方法就是 flushCallbacks
2、flushCallbacks方法灰常簡單啊,大家肯定能看得懂啊
1、復制一遍 callbacks
2、把 原來 callbacks 清空
3、遍歷 復制的 callbacks ,然后逐個執(zhí)行
var callbacks = []; var pending =false; functionflushCallbacks(){ pending =false; varcopies = callbacks.slice(0); callbacks.length =0; for(vari =0; i < copies.length; i++) { copies[i](); } }
這個方法是 直接傳給 上面設(shè)置的 宏任務(wù)函數(shù) 和 微任務(wù)函數(shù)的額
也就是說,宏任務(wù)和 微任務(wù) 的回調(diào),都是執(zhí)行這個 flushCallbacks
setTimeout(flushCallbacks)
嘿,我們之前有講過,Vue 會控制當時執(zhí)行棧的所有異步代碼只注冊一個 宏微任務(wù)
那么是怎么控制的呢?
還有還有,是怎么把 異步函數(shù) 存放到 callbacks 中的呢?
下面就需要請出我們的豬腳,nextTick 函數(shù)閃亮登場?。?!
3、NextTickVue.nextTick =function(cb, ctx){ callbacks.push(function(){ cb && cb.call(ctx); }); if(!pending) { pending =true; if(useMacroTask) { macroTimerFunc(); }else{ microTimerFunc(); } } }
通過判斷 pending 來確定是否需要注冊宏微任務(wù)
當?shù)谝淮巫缘臅r候,把 pending 設(shè)置為 true,表示任務(wù)隊列已經(jīng)在開始了,同一時期內(nèi)無需注冊了
然后在 任務(wù)隊列 執(zhí)行完畢之后,再把 pending 設(shè)置為 false(在 flushCallbacks 中)
你可以看到,就是在這里進行存放 異步函數(shù),還特地【包裝】了一遍,為了綁定一個上下文對象
Vue 怎么控制注冊宏任務(wù)還是微任務(wù)呢?
沒錯,就是這個鬼東西了,設(shè)置為 true 時注冊宏任務(wù),設(shè)置為false 注冊微任務(wù)
“在 2.6 版本中,已經(jīng)不存在這個鬼東西,全部使用了微任務(wù)注冊”
在 注冊 DOM 事件的時候用到,當事件回調(diào)執(zhí)行的過程中,所有的異步代碼都使用宏任務(wù)
你問為什么?內(nèi)容太多,會有專篇分析
然后,關(guān)于 macroTimerFunc 和 microTimerFunc 上文已經(jīng)講過啦,可以回去看看
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/110260.html
寫文章不容易,點個贊唄兄弟專注 Vue 源碼分享,文章分為白話版和 源碼版,白話版助于理解工作原理,源碼版助于了解內(nèi)部詳情,讓我們一起學習吧研究基于 Vue版本 【2.5.17】 如果你覺得排版難看,請點擊 下面鏈接 或者 拉到 下面關(guān)注公眾號也可以吧 【Vue原理】NextTick - 源碼版 之 服務(wù)Vue 初次看的兄弟可以先看 【Vue原理】NextTick - 白話版 簡單了解下...
摘要:這么講,有點籠統(tǒng),準確地說,應(yīng)該是事件回調(diào)執(zhí)行過程中,在主線程為空之后,異步代碼執(zhí)行之前,所有通過注冊的異步代碼都是用宏任務(wù)。 寫文章不容易,點個贊唄兄弟專注 Vue 源碼分享,文章分為白話版和 源碼版,白話版助于理解工作原理,源碼版助于了解內(nèi)部詳情,讓我們一起學習吧研究基于 Vue版本 【2.5.17】 如果你覺得排版難看,請點擊 下面鏈接 或者 拉到 下面關(guān)注公眾號也可以吧 【...
摘要:通常會做很多判斷來選擇存在的類型,比如判斷等是否存在,而選擇他為微任務(wù)類型但是可能宏微任務(wù)最后都是,因為他是保守兼容處理。 寫文章不容易,點個贊唄兄弟專注 Vue 源碼分享,文章分為白話版和 源碼版,白話版助于理解工作原理,源碼版助于了解內(nèi)部詳情,讓我們一起學習吧研究基于 Vue版本 【2.5.17】 如果你覺得排版難看,請點擊 下面鏈接 或者 拉到 下面關(guān)注公眾號也可以吧 【V...
摘要:的回調(diào)函數(shù)執(zhí)行的優(yōu)先級要高于,屬于觀察者。的回調(diào)函數(shù)保存在一個數(shù)組中,會將異步回調(diào)放到當前幀的末尾回調(diào)之前,如果過多,會導致回調(diào)不斷延后最后堆積太多。 阿里一面是電話面,問得不多,但是挺有深度。面試官一開始就說,看了你的項目,覺得你基礎(chǔ)挺好的,那我就不問基礎(chǔ)了。然后全程就真的沒有問一個基礎(chǔ)問題。。 1.說說你做的那個網(wǎng)頁版手機QQ項目的難點。 我首先想到了滾動條位置無法還原的問題,也就...
摘要:哪吒別人的看法都是狗屁,你是誰只有你自己說了才算,這是爹教我的道理。哪吒去他個鳥命我命由我,不由天是魔是仙,我自己決定哪吒白白搭上一條人命,你傻不傻敖丙不傻誰和你做朋友太乙真人人是否能夠改變命運,我不曉得。我只曉得,不認命是哪吒的命。 showImg(https://segmentfault.com/img/bVbwiGL?w=900&h=378); 出處 查看github最新的Vue...
閱讀 906·2021-09-22 15:17
閱讀 1936·2021-09-22 15:06
閱讀 2223·2021-09-08 09:35
閱讀 5113·2021-09-01 11:43
閱讀 3485·2019-08-30 15:55
閱讀 2159·2019-08-30 12:48
閱讀 3157·2019-08-30 12:45
閱讀 1791·2019-08-29 17:31