1.nextTick执行时,会将回调函数增加到数组中。当没有微工作(microtask)时,即pandingfalse,则通过调用timerFunc创立微工作。微工作会在宏工作(task)实现之后执行(解释为什么更改数据之后不会立刻更新页面)。微工作中会调用flushCallbacks,设置pendingtrue,示意微工作正在执行,下次nextTick须要从新创立微工作,而后顺次执行之前收集的回调函数。

var callbacks = [];// 微工作中将会调用的办法汇合var pending = false;// 是否创立了微工作var timerFunc;// 创立微工作的函数
function nextTick (cb, ctx) {  var _resolve;  callbacks.push(function () {// 将回到函数增加到数组中,将会在微工作中执行    if (cb) {      try {        cb.call(ctx);      } catch (e) {        handleError(e, ctx, 'nextTick');      }    } else if (_resolve) {      _resolve(ctx);    }  });  if (!pending) {// 无微工作    pending = true;    timerFunc();// 创立微工作  }  ...}
var p = Promise.resolve();timerFunc = function () {    p.then(flushCallbacks);// 创立微工作,微工作中调用之前收集的回调函数};
function flushCallbacks () {  pending = false;// 微工作已被执行  var copies = callbacks.slice(0);  callbacks.length = 0;  for (var i = 0; i < copies.length; i++) {    copies[i]();// 顺次执行回调函数  }}

2.更新页面的回调函数也会在微工作中执行。数据发生变化,告诉观察者更新,观察者接管到告诉后并不会立刻更新(lazysync默认为false,示意既不是懒执行也不是同步执行),而是调用queueWatcher将观察者(Watcher实例)增加到独自的数组(queue)中。flushSchedulerQueue函数会顺次调用数组中观察者的更新办法,flushSchedulerQueue函数会通过nextTick增加到微工作执行的数组中,之后会在微工作中被调用。观察者增加到数组时,通过flushing判断数组中观察者的更新办法是否曾经开始执行,如果未执行(flushing = false)就间接增加到数组中,否则将以后观察者增加到正在执行的观察者的前面,且要以后观察者的id正好大于后面观察者的id。所以以后观察者也会在本次微工作中执行。

// 观察者的update函数Watcher.prototype.update = function update () {  /* istanbul ignore else */  if (this.lazy) {    this.dirty = true;  } else if (this.sync) {    this.run();  } else {    queueWatcher(this);// 异步执行,在下一个微工作中执行  }};
ar MAX_UPDATE_COUNT = 100;var queue = [];// 微工作中将会执行更新操作的watcher汇合var activatedChildren = [];var has = {};// 标识尚未执行的watcher,如果曾经执行就能够持续增加var circular = {};var waiting = false;// 是否将外部执行watcher更新办法的函数(flushSchedulerQueue)增加到nextTick的微工作中var flushing = false;// 是否正在执行watcher的更新操作var index = 0;// 正在执行更新办法的watcher所在队列中的索引
function queueWatcher (watcher) {  var id = watcher.id;  if (has[id] == null) {    has[id] = true;    if (!flushing) {// 处于收集watcher状态,未执行watcher办法      queue.push(watcher);    } else {// 正在执行收集的watcher的更新办法      // if already flushing, splice the watcher based on its id      // if already past its id, it will be run next immediately.      var i = queue.length - 1;      while (i > index && queue[i].id > watcher.id) { // 插入到以后正在解决的wacher前面,且正好id要大于后面id        i--;      }      queue.splice(i + 1, 0, watcher); // 依据id大小插入到未解决的watch外面    }    // queue the flush    if (!waiting) {      waiting = true;      if (process.env.NODE_ENV !== 'production' && !config.async) {        flushSchedulerQueue();        return      }      nextTick(flushSchedulerQueue);    }  }}

3.flushSchedulerQueue函数会增加到微工作的数组中,并在微工作执行时被调用。调用过程中,先设置flushing = true,示意曾经开始执行收集的观察者更新办法,而后将观察者依据id从小到大进行排序,再执行更新办法(watcher.run),保障先执行父级观察者的更新办法,再调用子级观察者的更新办法。执行完所有的更新办法后,清空数组,并且设置waitingflushingfalse,别离示意下一次须要从新增加flushSchedulerQueue到微工作执行的数组中和以后未处于执行更新办法的状态(能够间接增加观察者到数组中)。最初执行观察者的updatedactivated生命周期函数。

function flushSchedulerQueue () {  currentFlushTimestamp = getNow();  flushing = true;  var watcher, id;  // Sort queue before flush.  // This ensures that:  // 1. Components are updated from parent to child. (because parent is always  //    created before the child)  // 2. A component's user watchers are run before its render watcher (because  //    user watchers are created before the render watcher)  // 3. If a component is destroyed during a parent component's watcher run,  //    its watchers can be skipped.  queue.sort(function (a, b) { return a.id - b.id; });// 从小到大排序  // do not cache length because more watchers might be pushed  // as we run existing watchers  for (index = 0; index < queue.length; index++) {    watcher = queue[index];    if (watcher.before) {      watcher.before();    }    id = watcher.id;    has[id] = null;// watcher办法曾经进入执行,能够持续增加该watcher    watcher.run();// 执行观察者的更新办法    ...  }  // keep copies of post queues before resetting state  var activatedQueue = activatedChildren.slice();  var updatedQueue = queue.slice();  /*     开始接管下一轮的观察者。  */  resetSchedulerState();  // call component updated and activated hooks  callActivatedHooks(activatedQueue);  callUpdatedHooks(updatedQueue);    ...}
/** * Reset the scheduler's state. */function resetSchedulerState () {  index = queue.length = activatedChildren.length = 0; // 革除数组  has = {};  if (process.env.NODE_ENV !== 'production') {    circular = {};  }  waiting = flushing = false;// 更新标识,下一次须要从新增加`flushSchedulerQueue`到微工作执行的数组中,以后未处于执行更新办法的状态(能够间接增加观察者到数组中)}

4.能够调用组件$nextTick办法执行nextTick函数,回调函数将在微工作中执行。

Vue.prototype.$nextTick = function (fn) {    return nextTick(fn, this)  };

5.参考文档

  • (微工作)[https://developer.mozilla.org...]