手摸手從0實現簡版Vue ---(批量更新&nextTick)

接:javascript

手摸手從0實現簡版Vue --- (對象劫持)vue

手摸手從0實現簡版Vue --- (數組劫持)java

手摸手從0實現簡版Vue --- (模板編譯)git

手摸手從0實現簡版Vue --- (依賴收集)github

1. 異步更新

假設咱們下面一種狀況,咱們在2s後屢次去修改某一個變量的值:api

setTimeout(() => {
  vm.msg = '1';
  vm.msg = '2';
  vm.msg = '3';
  vm.msg = '4';
}, 2000);
複製代碼

發現頁面正常顯示了msg的值,可是此時的數據更新致使的頁面更新了4次,顯然是不太合理的,咱們想要的是最終視圖只更新一次,因此咱們須要將以前的同步更新邏輯修改爲異步的。咱們首先將watcher放到一個數組中,首先用一個最簡單的辦法使用setTimeout在一輪任務執行結束後統一進行更新。數組

class Watcher {
  ... 
  update() {
    queueWatcher(this);
  }

  run() {
    console.log('數據更新');
    this.get()
  }
}

const queueIds = new Set();
let queue = [];
function flushQueue() {
  if (!queue.length) return;
  queue.forEach(watcher => watcher.run())
  queueIds.clear();
  queue = [];
}

function queueWatcher(watcher) {
  const id = watcher.id;
  if (!queueIds.has(id)) {
    queueIds.add(id);
    queue.push(watcher);
    setTimeout(flushQueue, 0);
  }
}
複製代碼

這也就簡單實現了視圖的批量更新操做。瀏覽器

2. nextTick

此時的頁面會統一進行一次刷新,可是Vue.$nextTick的實現並不僅僅是用setTimeout實現,nextTick內部一樣也是維護了一個事件隊列,等同步事件執行完畢後清空,就像咱們上面寫到的queueWatcher同樣,可是內部針對瀏覽器的api支持程度作了一些兼容和優化。異步

在異步隊列中,微任務的優先級更高,因此優先使用Promise而不是setTimeout,另外還有幾個異步的api,它們的優先級順序分別是:post

  • Promise(微任務)
  • MutationObserver(微任務)
  • setImmediate(宏任務)
  • setTimeout(宏任務)
const callbacks = []

function flushCallbacks() {
  callbacks.forEach(cb => cb())
}

export default function nextTick(cb) {
  callbacks.push(cb)

  const timerFunc = () => {
    flushCallbacks()
  }

  if (Promise) {
    return Promise.resolve().then(flushCallbacks)
  }

  if (MutationObserver) {
    const observer = new MutationObserver(timerFunc)
    const textNode = document.createTextNode('1')
    observer.observe(textNode, { characterData: true })
    textNode.textContent = '2'
    return
  }

  if (setImmediate) {
    return setImmediate(timerFunc)
  }

  setTimeout(timerFunc, 0)
}
複製代碼

而後將以前的setTimeout替換成nextTick看一下效果:

function queueWatcher(watcher) {
  const id = watcher.id;
  if (!queueIds.has(id)) {
    queueIds.add(id);
    queue.push(watcher);
+    nextTick(flushQueue, 0)
-    setTimeout(flushQueue, 0);
  }
}
複製代碼

效果相同,視圖也只更新了一次,實現了視圖的異步批量更新。

到如今爲止的話,咱們針對響應式原理已經作了基本功能的實現,後面咱們會去對computedwatch進行一下簡單的模擬。

代碼部分可看本次提交commit

相關文章
相關標籤/搜索