vue异步更新队列的理解

145 阅读1分钟

我们都知道vue的数据频繁变化,但dom只会更新一次,其实现原理是将多次更新保存到一个队列中,然后一次性更新,避免重复更新相同的dom。

但具体是如何实现的并不清楚。

一开始以为是通过防抖节流的方式实现,但明显存在很大问题,经查阅发现是通过nextTick利用浏览器事件循环实现的队列缓存、批量更新。

nextTick

vue nextTick优先使用Promise微任务实现,优先级:Promise > MutationObserver > setImmediate > setTimeout

浏览器事件循环

同时需要知道,一个事件循环中,先执行宏任务代码,宏任务中会产生Promise等微任务,微任务是在当前宏任务结束前才执行,所以当前宏任务中产生多个update数据更新,等到微任务时才会真正执行,同时时效性也非常高。

代码Demo

let queue = [];
let has = {};
let pending = false;
class Watch {
  update(obj) {
    queueWatcher(this, obj);
  }
  run() {
    console.log("run");
  }
}

function queueWatcher(watcher, obj) {
  const id = obj.id;
  if (!has[id]) {
    // push update队列
    queue.push(watcher);
    has[id] = true;
    if (!pending) {
      pending = true;
      // 生成微任务
      nextTick(flushSchedulerQueue);
    }
  }
}

function flushSchedulerQueue() {
  queue.forEach((watcher) => {
    watcher.run();
  });
  queue = [];
  has = {};
  pending = false;
}

function nextTick(flushCallbacks) {
  return Promise.resolve().then(flushCallbacks);
}

var watch1 = new Watch();
// 宏任务代码,会触发多次update,产生一个微任务
watch1.update({id: 1});
watch1.update({id: 1});// 2次id相同只会执行一次
watch1.update({id: 2});// id不同正常执行
// log: run run