vue中的nextTick实现原理

3,739 阅读2分钟

前言

根据数据的响应式原理,在数据变化之后会触发dom的更新。但是如果一个数据更新就引起dom的重新渲染,这样显然会导致大大降低性能。因此vue采用异步更新,更新数据后不能立刻拿到最新的节点,但是可以通过nextTick等待页面更新好之后再获取最终的dom。

数据更新

在vue中,数据的更新通过调用watcher上的update方法实现。而每一次调用update就可以把需要更新的watcher暂存起来,形成wathcer队列。

update() {
    // this.get();  // 渲染更新
    // 不要每次都调用get方法
    queueWatcher(this);  // 暂存的概念
  }

watcher队列

通过queueWatcher函数,将需要批量更新的watcher存在一个队列中,并做去重操作。flushScheduleQueue将队列中的watcher执行。这里的关键就是nextTick,它将watcher执行的操作变成了异步。

let queue = []; // 将需要批量更新的watcher存在一个队列中 稍后让watcher执行
let has = {};
let pending = false;
function flushScheduleQueue() {
  queue.forEach(watcher => {watcher.run(),watcher.cb()})
  queue = []
  has = {}
  pending = false
}
function queueWatcher(watcher) {
  const id = watcher.id
  if (has[id] == null) {
    queue.push(watcher)
    has[id] = true;  
    // 等待所有同步代码执行完毕之后再执行
    if (!pending) {
      nextTick(flushScheduleQueue)
      pending = true
    }
  }
}

nextTick

nextTick这个异步函数,不单是给watcher做内部调用,用户也可以通过$nextTick调用,但是整个异步的过程只会调用一次,因此需要把这些异步的操作暂存起来。

let callbacks = [];
function flushCallbacks() {
  while (callbacks.length) {
    let cb = callbacks.shift()
    cb()
  }
  pending = false
}

let pending = false;
export function nextTick(cb) { // 内部会调用nextTick 用户也会调用  但是异步只需要更新一次
  callbacks.push(cb);
  if(!pending) {
    timerFunc(); // 这个方法是异步方法  做了兼容性处理
    pending = true;
  }
}

根据上面的代码,我们只要通过函数timerFunc异步调用flushCallbacks函数就可以实现nextTick的异常更新,但是这个异步方法需要做兼容性处理。

nextTick的兼容性处理

let timerFunc;
if (Promise) {
  timerFunc = ()=>{
    Promise.resolve().then(flushCallbacks)
  }
}else if (MutationObserver) { // 可以监控DOM变化,并且是异步更新
  let observer = new MutationObserver(flushCallbacks);
  let textNode = document.createTextNode(1)
  observer.observe(textNode,{characterData:true})
  timerFunc = ()=>{
    textNode.textContent = 2;
  }
}else if (setImmediate) {
  timerFunc = ()=>{
    setImmediate(flushCallbacks)
  }
}else {
  timerFunc = ()=>{
    setTimeout(flushCallbacks);
  }
}

总结

nextTick实现的几个重点:

  • 1.将异步操作的函数暂存起来
  • 2.通过pending防止重复调用异步函数(类似防抖/节流)
  • 3.对异步操作做兼容性处理