Vue之nextTick内部实现

前言

最近开始阅读Vue相关源码,Vue整个实现比较复杂,所以这个系列的文章打算采用以点到面的方式来介绍Vue背后相关的实现,开篇就介绍nextTick。

具体细节

首先看看Vue中nextTick的使用,具体如下:

Vue.nextTick(function() {})
Vue.nextTick().then(() => {})
实例中$nextTick跟上面的使用相同

nextTick的作用是:

在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM。

nextTick

nextTick背后的实现逻辑流程如下:
这里写图片描述

上面是整个nextTick中大概的处理流程,实际上还有很多细节值得去推敲推敲,接下来就具体分析细节。

回调函数数组callbacks

  callbacks.push(function () {
    // 判断是否传入了回调函数,有则执行,无则判断是否支持Promise,调用resolve更改状态
    if (cb) {
      try {
        cb.call(ctx);
      } catch (e) {
        // 处理错误信息
        handleError(e, ctx, 'nextTick');
      }
    } else if (_resolve) {
      _resolve(ctx);
    }
  });

上面中需要注意的是_resolve,实际上这边的是Promise的resolve函数,具体代码如下:

  if (!cb && typeof Promise !== 'undefined') {
    return new Promise(function (resolve) {
      _resolve = resolve;
    })
  }

所以Vue根据是否传入回调函数,支持两种形式的回调

实际上这边的还有一个核心点是pending这边的处理。

pending相关的处理
pending本意即等待期间,此处也是用于控制每一次延迟回调的执行,具体的处理如下:

  // pending默认是false, 调用之后置为true
  // 当执行完callbacks之后再次置为false
  if (!pending) {
    pending = true;
    // 是否使用macroTask,默认是false
    if (useMacroTask) {
      macroTimerFunc();
    } else {
      microTimerFunc();
    }
  }

从上面的代码逻辑中可以看出,实际上这里就是处理callbacks,即延迟回调函数的执行。
但是这边支持两种形式的延迟:

  • macroTask
  • microTask

这里就提及下这两种形式的相关知识点:
首先就要提及JS的执行机制了,即事件循环机制

JS中异步代码都是在任务队列中,当主线程中的同步代码执行完毕后就会将任务队列中的代码进入主线程进行处理

任务队列是一个整体的概念,实际上它包括macro queue 和micro queue,就是上面两种任务的存放队列,macro queue存放的都是macroTask,相对的micro queue存放的是micro Task。

macrotasks有:

  • setTimeout
  • setInterval
  • setImmediate
  • I/O
  • UI渲染

microtasks有:

  • process.nextTick
  • promise
  • Object.observe
  • MutationObserver

在Vue源码中默认是使用micro task的:

microTasks的定义就对应着microTimerFunc
macroTasks的定义就对应着macroTimerFunc

首先来看看microTimerFunc的定义。

microTimerFunc
这里实际上的处理逻辑是:

  1. 首先判断是否支持Promise,优先使用Promise
  2. 如果不支持,实际上microTimerFunc = macroTimerFunc

当支持Promise时,就会调用相关的方法处理callbacks,如下:

function flushCallbacks () {
  pending = false;
  var copies = callbacks.slice(0);
  callbacks.length = 0;
  // 处理callbacks
  for (var i = 0; i < copies.length; i++) {
    copies[i]();
  }
}

macroTimerFunc
Vue对于该方法的实现实际上相对于microTimerFunc复杂了,实际上是分为三种:

  1. 首先判断是否支持setImmediate,支持则使用setImmediate来实现延迟回调
  2. 若不支持,则判断是否支持MessageChannel,支持则使用其来实现延迟回调
  3. 若不支持,则使用setTimeout来实现回调,即setTimeout(flushCallbacks, 0)

总结

实际上整个Vue.nextTick背后的处理如下:

  1. 默认使用microTask实现延迟回调
  2. microTask则分为两种情形:支持Promise和不支持Promise
  3. 支持Promise的话直接Promise.resolve().then(flushCallbacks)
  4. 不支持Promise,实际上是调用macroTimerFunc,即macroTask
  5. macroTimerFunc的实现具体分为3种:setImmediate、MessageChannel、setTimeout

猜你喜欢

转载自blog.csdn.net/s1879046/article/details/81301668