在vue的體系中惕医,數據的雙向綁定主要通過<code>Object.defineProperty</code>進行數據從模型到視圖的流動及通過DOM事件進行數據從視圖到模型的流動。這些都比較容易理解龙优,但是如果頻繁的更改模型的話,就會造成頻繁的更新View事秀,會造成頻繁的dom操作彤断,這是比較耗時的,因此需要進行優(yōu)化易迹,Vue2.0是通過批處理進行優(yōu)化的宰衙,而異步執(zhí)行隊列,是批處理中一個很重要的一個環(huán)節(jié)睹欲。
在vue.js(Vue.js v2.1.10)416-490行供炼,描述了這一神奇的異步執(zhí)行隊列一屋。下面我們將進行相關的代碼分析。
- 1.函數整體
var nextTick =(function(){
return function queueNextTick (cb, ctx) {}
})()
我們可以看到整體來說袋哼,這是一個由立即執(zhí)行函數構成的冀墨,返回值是函數的一個函數,我們可以使用下面的測試代碼
var nextTick =(function(){
return function queueNextTick (cb, ctx) {
console.log(cb);
}
})();
nextTick("this is test1")
- 2.函數內容
var nextTick = (function () {
var callbacks = [];
var pending = false;
var timerFunc;
function nextTickHandler () {
}
if (typeof Promise !== 'undefined' && isNative(Promise)) {
var p = Promise.resolve();
var logError = function (err) { console.error(err); };
timerFunc = function () {
p.then(nextTickHandler).catch(logError);
if (isIOS) { setTimeout(noop); }
};
} else if (typeof MutationObserver !== 'undefined' && (
isNative(MutationObserver) ||
MutationObserver.toString() === '[object MutationObserverConstructor]'
)) {
var counter = 1;
var observer = new MutationObserver(nextTickHandler);
var textNode = document.createTextNode(String(counter));
observer.observe(textNode, {
characterData: true
});
timerFunc = function () {
}
return function queueNextTick (cb, ctx) {
}
})();
我們看到在函數內部涛贯,有三個函數诽嘉,nextTickHandler,timerFunc弟翘,以及queueNextTick 虫腋。
其中
if(typeof Promise !== 'undefined' && isNative(Promise)){
}
else if(typeof MutationObserver !== 'undefined' && (
isNative(MutationObserver) ||
MutationObserver.toString() === '[object MutationObserverConstructor]'){
}else{
}
是用來判斷當前執(zhí)行環(huán)境,給timerFunc 選擇一個合適的執(zhí)行條件衅胀。如果是支持Promise的環(huán)境中岔乔,timerFunc 中則用<code>p.then(nextTickHandler)</code>和 <code>setTimeout(nextTickHandler, 0);</code>執(zhí)行回調;如果是MutationObserver環(huán)境則通過修改文本節(jié)點的值的變化觸發(fā)調用的的滚躯。關于為什么優(yōu)先使用Promise雏门,知乎里也在討論這件事,點這里看詳情掸掏。
- 3.函數的返回函數
function queueNextTick (cb, ctx) {
var _resolve;
callbacks.push(function () {
if (cb) { cb.call(ctx); }
if (_resolve) {
console.log(0);
_resolve(ctx); }
});
if (!pending) {
pending = true;
timerFunc();
}
if (!cb && typeof Promise !== 'undefined') {
return new Promise(function (resolve) {
console.log("now")
_resolve = resolve;
})
}
}
當我們使用<code>nextTick(function Test(){console.log("1" +this.isIOS);},this)</code>時茁影,我們可以發(fā)現 Test封裝后被放進了回調隊列callbacks中,
if (cb) { cb.call(ctx); }
if (_resolve) {
console.log(0);
_resolve(ctx); }
注意這里的_resolve丧凤,他是在
if (!cb && typeof Promise !== 'undefined') {
return new Promise(function (resolve) {
_resolve = resolve;
})
}
里面進行了賦值募闲,因為promise注冊函數是立即執(zhí)行的,因此在返回前給當前的域中的_resolve賦了值愿待。當進行回調時浩螺,便可以使用_resolve的值。我們可以使用執(zhí)行空函數進行測試:
console.log(nextTick();)//打印是Promise的相關內容
- 4.nextTickHandler隊列回調控制函數
function nextTickHandler () {
pending = false;
//對callbacks進行深復制
var copies = callbacks.slice(0);
callbacks.length = 0;
for (var i = 0; i < copies.length; i++) {
if(i==0){
copies[i]();
}
copies[i]();
}
}
此函數便是在觸發(fā)時把回調隊列中函數依次調用仍侥,特別注意 pending=flase這句話用來把執(zhí)行回調過程中新加進回調數組的計入下一個tick要出,另外需要注意<code> var copies =callbacks.slice(0); callbacks.length = 0;
</code>這句話,用的很好农渊,首先對回掉數組進行深復制患蹂,然后把callbacks置空余掖。之所以不用callbacks=[]考抄,是因為設置length=0可以不改變原有對象的地址怠蹂。
- 5.如何實現隊列的功能
我們使用測試代碼:
nextTick().then(()=>{
console.log("last");
})
var ss={
isIOS:"sss"
}
nextTick(function(){
console.log("1" +this.isIOS);
},ss)
最后我畫出了隊列圖