浅谈Vuejs中nextTick()异步更新队列源码解析

2020-06-16 06:24:03易采站长站整理

)) {
// use MutationObserver where native Promise is not available,
// e.g. PhantomJS IE11, iOS7, Android 4.4
var counter = 1;
var observer = new MutationObserver(nextTickHandler);
//创建一个textnode dom节点,并让MutationObserver 监视这个节点;而 timeFunc正是改变这个dom节点的触发函数
var textNode = document.createTextNode(String(counter));
observer.observe(textNode, {
characterData: true
});
timerFunc = function () {
counter = (counter + 1) % 2;
textNode.data = String(counter);
};
} else {// 上面两种不支持的话,就使用setTimeout

timerFunc = function () {
setTimeout(nextTickHandler, 0);
};
}
//nextTick接受的函数, 参数1:回调函数 参数2:回调函数的执行上下文
return function queueNextTick (cb, ctx) {
var _resolve;//用于接受触发 promise.then中回调的函数
//向回调数据中pushcallback
callbacks.push(function () {
//如果有回调函数,执行回调函数
if (cb) { cb.call(ctx); }
if (_resolve) { _resolve(ctx); }//触发promise的then回调
});
if (!pending) {//是否执行刷新callback队列
pending = true;
timerFunc();
}
//如果没有传递回调函数,并且当前浏览器支持promise,使用promise实现
if (!cb && typeof Promise !== 'undefined') {
return new Promise(function (resolve) {
_resolve = resolve;
})
}
}
})();

 我在注释中解释了nextTick()函数的逻辑

上面处理回调的三个方式的使用优先级的原因:因为Promise和MutationObserver和触发的事件在同一个事件循环里面(只不过是运行在微观队列里面),但是setTimeout的回调函数是运行在下次时间循环里面。

优先使用Promise的原因是MutationObserver在ios9.3.3以上版本的UIWebview中运行一段时间后就停止了。
上面代码的注释已经完全说明了代码逻辑。简单理解:将callback 推到队列里面,如果还没有执行过在下次事件循环执行触发callback函数。

注意: 如果使用nextTick()不设置回调函数,而是使用Promise的方式设置回调函数,里面this并不是指向当前的Vue实例,而是指向window(严格模式是undefined);
但是通过上面的分析可知:执行上下文是通过Promise.then()里的回调函数的第一个参数传递的。

nextTick()被使用的地方

1、他是全局Vue的一个函数,因此我们可以通过vue直接调用。

2、Vue系统中,用于处理dom更新的操作

Vue中有一个watcher,用于观察数据的变化,然后更新dom。前面我们就知道Vue里面不是每一次数据改变都会触发更新dom,而是将这些操作都缓存在一个队列,在一个事件循环结束之后,刷新队列,统一执行dom更新操作。