共计 3915 个字符,预计需要花费 10 分钟才能阅读完成。
1.nextTick
执行时,会将回调函数增加到数组中。当没有微工作 (microtask) 时,即 panding
为false
, 则通过调用 timerFunc
创立微工作。微工作会在宏工作(task)实现之后执行 (解释为什么更改数据之后不会立刻更新页面)。微工作中会调用flushCallbacks
,设置pending
为true
,示意微工作正在执行,下次 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. 更新页面的回调函数也会在微工作中执行。数据发生变化,告诉观察者更新,观察者接管到告诉后并不会立刻更新(lazy
和 sync
默认为 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
),保障先执行父级观察者的更新办法,再调用子级观察者的更新办法。执行完所有的更新办法后,清空数组,并且设置waiting
和flushing
为 false
,别离示意下一次须要从新增加flushSchedulerQueue
到微工作执行的数组中和以后未处于执行更新办法的状态(能够间接增加观察者到数组中)。最初执行观察者的 updated
、activated
生命周期函数。
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…]