<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          「源碼剖析」nextTick到底有什么作用

          共 2614字,需瀏覽 6分鐘

           ·

          2021-02-02 07:37

          點(diǎn)擊上方“前端簡(jiǎn)報(bào)”,選擇“設(shè)為星標(biāo)

          第一時(shí)間關(guān)注技術(shù)干貨!


          在vue中每次監(jiān)聽(tīng)到數(shù)據(jù)變化的時(shí)候,都會(huì)去調(diào)用notify通知依賴(lài)更新,觸發(fā)watcher中的update方法。

          ?update?()?{
          ????/*?istanbul?ignore?else?*/
          ????if?(this.lazy)?{
          ?????
          ????}?else?if?(this.sync)?{
          ???
          ????}?else?{
          ??????this.get()
          ??????//queueWatcher(this)
          ????}
          ??}

          如果通過(guò)watcher中的get方法去重新渲染組件,那么在渲染的過(guò)程中假如多次更新數(shù)據(jù)會(huì)導(dǎo)致同一個(gè)watcher被觸發(fā)多次,這樣會(huì)導(dǎo)致重復(fù)的數(shù)據(jù)計(jì)算和DOM的操作。如下圖所示,修改3次message之后DOM被操作了3次。

          為了解決上述問(wèn)題,不去直接調(diào)用get方法而是將每次調(diào)用update方法后需要批處理的wather暫存到一個(gè)隊(duì)列當(dāng)中,如果同一個(gè) watcher 被多次觸發(fā),通過(guò)wacther 的id屬性對(duì)其去重,只會(huì)被推入到隊(duì)列中一次。然后,等待所有的同步代碼執(zhí)行完畢之后在下一個(gè)的事件循環(huán)中,Vue 刷新隊(duì)列并執(zhí)行實(shí)際 (已去重的) 工作。

          let?has:?{?[key:?number]:??true?}?=?{}
          let?waiting?=?false
          export?function?queueWatcher?(watcher:?Watcher)?{
          ??const?id?=?watcher.id //對(duì)watcher去重
          ??if?(has[id]?==?null)?{
          ????has[id]?=?true
          ????queue.push(watcher);
          ????if?(!waiting)?{ //節(jié)流
          ??????waiting?=?true
          ??????nextTick(flushSchedulerQueue)
          ????}
          }

          調(diào)用watcher的run方法異步更新DOM

          let?has:?{?[key:?number]:??true?}?=?{}
          function?flushSchedulerQueue?()?{
          ??let?watcher,?id
          ??queue.sort((a,?b)?=>?a.id?-?b.id)

          ??for?(index?=?0;?index?????watcher?=?queue[index]
          ????if?(watcher.before)?{
          ??????watcher.before()
          ????}
          ????id?=?watcher.id
          ????has[id]?=?null //清空id
          ????watcher.run() //更新值
          ??}
          ??
          ??resetSchedulerState() //清空watcher隊(duì)列
          }

          function?resetSchedulerState?()?{
          ??index?=?queue.length??=?0
          ??has?=?{}
          ??waiting?=??false
          }

          在vue內(nèi)部調(diào)用nextTick(flushSchedulerQueue)vm.$nextTick方法調(diào)用的也是nextTick()方法

          ?Vue.prototype.$nextTick?=?function?(cb)?{
          ????nextTick(cb,this);
          ??};

          那么多次調(diào)用nextTick方法是怎么處理的呢?

          const?callbacks?=?[]
          let?pending?=?false?
          export?function?nextTick?(cb?:?Function,?ctx?:?Object)?{
          ??callbacks.push(()?=>?{
          ????if?(cb)?{
          ??????try?{
          ????????cb.call(ctx)
          ??????}?catch?(e)?{
          ????????handleError(e,?ctx,?'nextTick')
          ??????}
          ????}
          ??})
          ??if?(!pending)?{
          ????pending?=?true
          ????timerFunc()
          ??}
          }

          nextTick將所有的回調(diào)函數(shù)暫存到了一個(gè)隊(duì)列中,然后通過(guò)異步調(diào)用更新去依次執(zhí)行隊(duì)列中的回調(diào)函數(shù)。

          function?flushCallbacks?()?{
          ??pending?=?false
          ??const?copies?=?callbacks.slice(0)
          ??callbacks.length?=?0
          ??for?(let?i?=?0;?i?????copies[i]()
          ??}
          }

          nextTick函數(shù)中異步更新對(duì)兼容性做了處理,使用原生的?Promise.thenMutationObserver?和?setImmediate,如果執(zhí)行環(huán)境不支持,則會(huì)采用?setTimeout(fn, 0)?代替。

          Promise

          if?(typeof?Promise?!==?'undefined'?&&?isNative(Promise))?{
          ??const?p?=?Promise.resolve()
          ??timerFunc?=?()?=>?{
          ????p.then(flushCallbacks)
          ??}
          }

          ? ?

          MutationObserver

          MutationObserver 它會(huì)在指定的DOM發(fā)生變化時(shí)被調(diào)用。創(chuàng)建了一個(gè)文本DOM,通過(guò)監(jiān)聽(tīng)字符值的變化,當(dāng)文本字符發(fā)生變化的時(shí)候調(diào)用回調(diào)函數(shù)。

          if?(!isIE?&&?typeof?MutationObserver?!==?'undefined'?&&?(
          ??isNative(MutationObserver)?||
          ??MutationObserver.toString()?===?'[object?MutationObserverConstructor]'
          ))?{
          ??let?counter?=?1
          ??const?observer?=?new?MutationObserver(flushCallbacks)
          ??const?textNode?=?document.createTextNode(String(counter))
          ??observer.observe(textNode,?{
          ????characterData:?true
          ??})
          ??timerFunc?=?()?=>?{
          ????counter?=?(counter?+?1)?%?2
          ????textNode.data?=?String(counter)
          ??}
          }

          setImmediate

          setImmediate該方法用作把一些需要持續(xù)運(yùn)行的操作放在一個(gè)其他函數(shù)里,在瀏覽器完成后面的其他語(yǔ)句后,就立即執(zhí)行此替換函數(shù)。

          if?(typeof?setImmediate?!==?'undefined'?&&?isNative(setImmediate))?{
          ??timerFunc?=?()?=>?{
          ????setImmediate(flushCallbacks)
          ??}
          }else{
          ??timerFunc?=?()?=>?{
          ????setTimeout(flushCallbacks,?0)
          ??}
          }



          總結(jié)





          vue渲染DOM的時(shí)候觸發(fā)set方法中的去依賴(lài)更新,在更新的過(guò)程中watcher不是每次都去執(zhí)行去觸發(fā)DOM的更新,而是通過(guò)對(duì)wather的去重之后,通過(guò)nextTick異步調(diào)用觸發(fā)DOM更新。

          nextTick()就是一個(gè)異步函數(shù),在異步函數(shù)中通過(guò)隊(duì)列批處理nextTick傳入的回調(diào)函數(shù)cb,但是隊(duì)列彼此不是同時(shí)進(jìn)行的,通過(guò)節(jié)流的方式依次執(zhí)行。



          END


          精選推薦


          「源碼剖析」如何實(shí)現(xiàn)一個(gè)虛擬DOM算法

          前端簡(jiǎn)報(bào)

          02?Feb?2020

          瀏覽 73
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  日本女人高潮视频 | 黄色成人在线观看 | 国产精品人妻无码久久久郑州天气网 | 免费在线观看无码视频 | 五月天色婷婷综合 |