Vue 的 nextTick 是怎么做的

背景

很多人对 process.nextTick() 有个误解,认为它也是前端常用的微任务,但其实不是。

这个方法只是在 Node 中存在,并且是个 Node 进入事件轮询的下一阶段时就会触发的一个神奇的方法。

那么问题来了,Vue 中也实现过一个 this.$nextTick(),这是怎么做的呢?

结论

  • 优先使用 Promise API;
  • 若不能用 Promise API,次选用 MutationObserver API,一般见于 PhantomJS IE11, iOS7, Android 4.4;
  • 若还是不能用 MutationObserver API,那就 setTimeout,见于更低版本的IE8,9,10。

提一句在浏览器环境中 :

常见的 micro task 有 MutationObsever 和 Promise.then

常见的 macro task 有 setTimeout、MessageChannel、postMessage、setImmediate

附录

Vue 2.6.2 源码

export const nextTick = (function () {
  // ...
  if (typeof Promise !== 'undefined' && isNative(Promise)) {
    var p = Promise.resolve()
    var logError = err => { console.error(err) }
    timerFunc = () => {
      p.then(nextTickHandler).catch(logError)
      // in problematic UIWebViews, Promise.then doesn't completely break, but
      // it can get stuck in a weird state where callbacks are pushed into the
      // microtask queue but the queue isn't being flushed, until the browser
      // needs to do some other work, e.g. handle a timer. Therefore we can
      // "force" the microtask queue to be flushed by adding an empty timer.
      if (isIOS) setTimeout(noop)
    }
  } else if (typeof MutationObserver !== 'undefined' && (
    isNative(MutationObserver) ||
    // PhantomJS and iOS 7.x
    MutationObserver.toString() === '[object MutationObserverConstructor]'
  )) {
    // use MutationObserver where native Promise is not available,
    // e.g. PhantomJS IE11, iOS7, Android 4.4
    var counter = 1
    var observer = new MutationObserver(nextTickHandler)
    var textNode = document.createTextNode(String(counter))
    observer.observe(textNode, {
      characterData: true
    })
    timerFunc = () => {
      counter = (counter + 1) % 2
      textNode.data = String(counter)
    }
  } else {
    // fallback to setTimeout
    /* istanbul ignore next */
    /*使用setTimeout将回调推入任务队列尾部*/
    timerFunc = () => {
      setTimeout(nextTickHandler, 0)
    }
  }

  /*
    推送到队列中下一个tick时执行
    cb 回调函数
    ctx 上下文
  */
  return function queueNextTick (cb?: Function, ctx?: Object) {
    // ...
  }
})()
posted @ 2020-03-25 10:47  Ever-Lose  阅读(186)  评论(0编辑  收藏  举报