<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>

          Vue的批量更新原理

          共 3498字,需瀏覽 7分鐘

           ·

          2021-08-12 18:17


          簡(jiǎn)單的代碼開(kāi)始:

          var app = new Vue({  el: '#app',  data: {    message: 'Hello Vue!'  },  watch: {    message (val) {      console.log('mutation')    }  },  mounted () {    this.message = 1    this.message = 2    this.message = 3  }})


          首先看這個(gè)例子中,連續(xù)3次觸發(fā)了mutation,那么watch中的cb會(huì)被執(zhí)行幾次呢?


          答案是一次。


          那么為什么會(huì)是一次呢?本文會(huì)圍繞著這個(gè)問(wèn)題的解釋來(lái)粗淺地討論一下Vue中批量更新的原理。


          首先要知道,msg這個(gè)key,是通過(guò)Object.defineProperty被監(jiān)聽(tīng)了的,Vue通過(guò)這個(gè)api實(shí)現(xiàn)在key被set的時(shí)候(也就是this.msg = xxx這種操作),觸發(fā)所有訂閱了這個(gè)key的Watcher的update方法。


          這里引入了一個(gè)Watcher的概念,那么這個(gè)Watcher是什么呢?


          從語(yǔ)義上理解,Wathcer其實(shí)類(lèi)似于一個(gè)key的觀察者,當(dāng)key被set的時(shí)候,Watcher會(huì)調(diào)用自身的update方法。


          在 mountComponent 也就是加載組件的時(shí)候會(huì)調(diào)用 Watcher 創(chuàng)建觀察者。


          new Watcher(vm, updateComponent, noop, {  before () {    if (vm._isMounted && !vm._isDestroyed) {      callHook(vm, 'beforeUpdate')    }  }}, true /* isRenderWatcher */)


          監(jiān)聽(tīng)到一個(gè)set引起的mutation就立即同步執(zhí)行一次cb嗎?

          顯然是不可以的。


          舉個(gè)例子:

          for (let i = 0; i < 1000; i++) {   this.msg = `循環(huán)了${i}次`;}


          假如對(duì)于這種操作,1000次里面每次Watcher都要響應(yīng)執(zhí)行一次update,那可能是有很大的性能開(kāi)銷(xiāo)的。像本文的這個(gè)例子中update其實(shí)就是一個(gè)console.log,但是如果cb是一個(gè)開(kāi)銷(xiāo)比較大的方法,那么就可能會(huì)引起性能問(wèn)題了。


          所以u(píng)pdate操作一定是異步的。


          雖然知道要通過(guò)異步來(lái)解決,但具體是如何解決的呢?Vue的做法是把調(diào)用cb放到了一個(gè)micro task或者macro task隊(duì)列中,具體放到微任務(wù)隊(duì)列還是宏任務(wù)隊(duì)列要看當(dāng)前的運(yùn)行環(huán)境是否支持Promise、MutationObserver、setImmediate這幾個(gè)相當(dāng)于放入微任務(wù)隊(duì)列的api,支持就會(huì)放在微任務(wù)隊(duì)列,不支持則使用setTimeout這個(gè)api把調(diào)用cb放到宏任務(wù)隊(duì)列里。


          不管放到微任務(wù)隊(duì)列還是宏任務(wù)隊(duì)列,調(diào)用cb都會(huì)在所有的同步代碼執(zhí)行完畢后執(zhí)行。這一點(diǎn)涉及到event loop的知識(shí),因?yàn)榭偸窍葓?zhí)行所有的同步代碼,然后從微任務(wù)隊(duì)列中按順序執(zhí)行,微任務(wù)隊(duì)列空了才會(huì)從宏任務(wù)隊(duì)列中取出一條執(zhí)行。如果此時(shí)微任務(wù)隊(duì)列還有任務(wù),那么就會(huì)繼續(xù)按照這個(gè)循環(huán)執(zhí)行,這個(gè)就是event loop。


          通俗地理解Vue的行為就是在監(jiān)聽(tīng)到key的mutation之后key的Watcher都會(huì)觸發(fā)update,想要調(diào)用自身的cb屬性,但是Vue僅僅是答應(yīng)會(huì)在未來(lái)的某個(gè)時(shí)刻執(zhí)行Watcher的這個(gè)update請(qǐng)求也就是調(diào)用它的cb,并且在調(diào)用之前都不會(huì)再受理該Watcher的update的請(qǐng)求。


          下面看看源碼

          function queueWatcher (watcher) {  const id = watcher.id  if (has[id] == null) {    has[id] = true    if (!flushing) {      queue.push(watcher)    } else {      let i = queue.length - 1      while (i > index && queue[i].id > watcher.id) {        i--      }      queue.splice(i + 1, 0, watcher)    }    if (!waiting) {      waiting = true
          if (process.env.NODE_ENV !== 'production' && !config.async) { flushSchedulerQueue() return } nextTick(flushSchedulerQueue) } }}

          重點(diǎn)是 nextTick 源碼

          let timerFunc
          if (typeof Promise !== 'undefined' && isNative(Promise)) { const p = Promise.resolve() timerFunc = () => { p.then(flushCallbacks) if (isIOS) setTimeout(noop) } isUsingMicroTask = true} else 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) } isUsingMicroTask = true} else if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) { timerFunc = () => { setImmediate(flushCallbacks) }} else { timerFunc = () => { setTimeout(flushCallbacks, 0) }}
          function nextTick (cb, ctx) { let _resolve callbacks.push(() => { if (cb) { try { cb.call(ctx) } catch (e) { handleError(e, ctx, 'nextTick') } } else if (_resolve) { _resolve(ctx) } }) if (!pending) { pending = true timerFunc() } if (!cb && typeof Promise !== 'undefined') { return new Promise(resolve => { _resolve = resolve }) }}


          瀏覽 27
          點(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>
                  欧美日韩A片黄色电影视频 | 亚洲视频VS在线免费观看 | 天天日天天日天天日 | 国产65一二三区 | 影音先锋成人AV资源网 |