關(guān)于vue的nextTick

??在vue的代碼中,有時(shí)候會(huì)用到this.$nextTick,這個(gè)方法的回調(diào)函數(shù)里可以獲取到數(shù)據(jù)更新之后的DOM,使用的方法這里就不說(shuō)了。在vue中可以簡(jiǎn)單理解為nextTick是下一次dom更新。
??另外,在我之前寫(xiě)的源碼中,關(guān)于Watcher中的update方法只寫(xiě)了簡(jiǎn)單的同步更新,在vue的源碼中,update方法,除非watcher中指定了sync為true,否則都為異步更新。異步更新會(huì)通過(guò)queueWatcher把當(dāng)前觀察者加入一個(gè)全局的watcher隊(duì)列中,該隊(duì)列中的watcher都會(huì)在nextTick后統(tǒng)一調(diào)用。

事件循環(huán)(關(guān)于macroTask與microTask)

??簡(jiǎn)單來(lái)說(shuō),事件循環(huán)是這樣的,先執(zhí)行一個(gè)macroTask,然后執(zhí)行完microTask隊(duì)列中所有的microTask,然后再去執(zhí)行一個(gè)macroTask。
??macroTask有這幾種:setTimeout,setInterval,setImmediate,postMessage,MessageChannel,I/O,UI渲染等。
??microTask有這幾種:原生Promise,MutationObserver,process.nextTick(Node環(huán)境)
看了下源碼,在vue2.5.0之前,vue對(duì)nextTick的處理都是這樣的:
1.是否有原生的Promise對(duì)象,如果是,就使用Promise.then異步觸發(fā)nextTick
2.如果不支持Promise,就使用MutationObserver來(lái)異步觸發(fā)nextTick
3.如果都不支持,則使用setTimeout。
??總而言之,從大部分的瀏覽器來(lái)說(shuō),所有的nextTick都是一個(gè)microTask。
??但是,如果去看最新的vue源碼,就會(huì)發(fā)現(xiàn)獨(dú)立了一個(gè)next-tick.js文件出來(lái),里面的邏輯有了一些改變。
至于改動(dòng)的原因,源碼中也有說(shuō)明,我試著翻譯了一下:

// Here we have async deferring wrappers using both microtasks and (macro) tasks.
// In < 2.4 we used microtasks everywhere, but there are some scenarios where
// microtasks have too high a priority and fire in between supposedly
// sequential events (e.g. #4521, #6690) or even between bubbling of the same
// event (#6566). However, using (macro) tasks everywhere also has subtle problems
// when state is changed right before repaint (e.g. #6813, out-in transitions).
// Here we use microtask by default, but expose a way to force (macro) task when
// needed (e.g. in event handlers attached by v-on).
// 
// 這里我們的異步函數(shù)一起使用了microtasks和macrotasks,
// 在2.4版本之前,我們?cè)谌魏蔚胤蕉际怯昧薽icrotasks,但是這里是一些microtasks有錯(cuò)誤的場(chǎng)景
// 由于microtasks有著太高的優(yōu)先級(jí),它在兩次本應(yīng)該順序執(zhí)行的事件之間觸發(fā)了(比如 #4521, #6690),
// 甚至在同一個(gè)事件冒泡的過(guò)程之間觸發(fā)了(比如#6566)。
// 然而,所有的地方都使用macroTask也會(huì)有一些小問(wèn)題,比如當(dāng)狀態(tài)剛好在瀏覽器重繪之前改變(#6813)
// 因此,我們默認(rèn)使用了microtask,但是提供了一個(gè)方法強(qiáng)制使用macroTask如果需要的話。(比如在事件的綁定中)

關(guān)于事件冒泡的情況,我寫(xiě)了如下一個(gè)小demo:

  <div id="a">
    aaaaa
    <div id="b">bbbbb</div>
  </div>
  <script>
    var $a = document.querySelector('#a')
    var $b = document.querySelector('#b')
    $b.addEventListener('click', function () {
      Promise.resolve()
        .then(function () {
          console.log('microTask')
        })
      console.log('b')
    })
    $a.addEventListener('click', function () {
      console.log('a')
    })
  </script>

??按理說(shuō)nextTick應(yīng)該在點(diǎn)擊完b然后更新完DOM之后執(zhí)行,但是如果使用了microTask,就像上面的代碼,輸出的結(jié)果為:b,microTask,a,也就是說(shuō)冒泡到a上面的事件里如果有狀態(tài)的改變,那么在nextTick中便無(wú)法獲得,其余的場(chǎng)景原理應(yīng)該也是差不多。如果把這里的microTask換成一個(gè)macroTask,那么在nextTick中就能獲得這兩個(gè)事件之后的DOM。另外,據(jù)我推測(cè),涉及到事件的,不管是冒泡還是捕獲的,應(yīng)該都是一個(gè)macroTask。
??說(shuō)回源碼,為了解決這個(gè)問(wèn)題,vue2.5之后,next-tick.js中有這樣一段代碼:

export function withMacroTask (fn: Function): Function {
  return fn._withTask || (fn._withTask = function () {
    useMacroTask = true
    const res = fn.apply(null, arguments)
    useMacroTask = false
    return res
  })
}

?這個(gè)方法的作用是,回調(diào)函數(shù)(fn)中的狀態(tài)改變引起的nextTick操作,異步任務(wù)使用macroTask方法,具體的實(shí)現(xiàn)可以參照源碼:源碼。全局去搜索withMacroTask,可以發(fā)現(xiàn)目前只有events模塊使用了這個(gè)方法。
?另外,最新的源碼中,使用的macroTask的優(yōu)先級(jí)為:setImmediate(只有IE支持),MessageChannel,setTimeout

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 前言 見(jiàn)解有限,如有描述不當(dāng)之處,請(qǐng)幫忙及時(shí)指出,如有錯(cuò)誤,會(huì)及時(shí)修正。 ----------超長(zhǎng)文+多圖預(yù)警,需...
    hnscdg閱讀 2,457評(píng)論 0 32
  • 1.安裝 可以簡(jiǎn)單地在頁(yè)面引入Vue.js作為獨(dú)立版本,Vue即被注冊(cè)為全局變量,可以在頁(yè)面使用了。 如果希望搭建...
    Awey閱讀 11,278評(píng)論 4 129
  • 這篇筆記主要包含 Vue 2 不同于 Vue 1 或者特有的內(nèi)容,還有我對(duì)于 Vue 1.0 印象不深的內(nèi)容。關(guān)于...
    云之外閱讀 5,168評(píng)論 0 29
  • 周二上午參加孩子在幼兒園階段的最后一次家長(zhǎng)開(kāi)放日活動(dòng) 中午在小區(qū)吃完飯走到郵局繳納交通罰款未成功 買(mǎi)花亦為成功 見(jiàn)...
    無(wú)憂籽閱讀 87評(píng)論 0 0
  • 顧秋萍 紹興可飛家紡有限公司 【日精進(jìn)打卡第10天】 【知~學(xué)習(xí)】 《六項(xiàng)精進(jìn)》1遍共12遍 《大學(xué)》1遍共11遍...
    sunny2157閱讀 247評(píng)論 0 0

友情鏈接更多精彩內(nèi)容