這篇文章主要介紹“vue頁面渲染是異步的嗎”的相關(guān)知識,小編通過實際案例向大家展示操作過程,操作方法簡單快捷,實用性強,希望這篇“vue頁面渲染是異步的嗎”文章能幫助大家解決問題。
創(chuàng)新互聯(lián)是專業(yè)的濠江網(wǎng)站建設(shè)公司,濠江接單;提供成都網(wǎng)站制作、做網(wǎng)站、外貿(mào)營銷網(wǎng)站建設(shè),網(wǎng)頁設(shè)計,網(wǎng)站設(shè)計,建網(wǎng)站,PHP網(wǎng)站建設(shè)等專業(yè)做網(wǎng)站服務(wù);采用PHP框架,可快速的進(jìn)行濠江網(wǎng)站開發(fā)網(wǎng)頁制作和功能擴展;專業(yè)做搜索引擎喜愛的網(wǎng)站,專業(yè)的做網(wǎng)站團(tuán)隊,希望更多企業(yè)前來合作!
vue頁面渲染是異步的。vue采用的是異步渲染,這樣可以提升性能;如果不采用異步更新,在每次更新數(shù)據(jù)都會對當(dāng)前組件進(jìn)行重新渲染,為了性能考慮,Vue會在本輪數(shù)據(jù)更新后,再去異步更新視圖。
vue頁面渲染是異步的。
Vue
在更新DOM
時是異步執(zhí)行的,只要偵聽到數(shù)據(jù)變化,Vue
將開啟一個隊列,并緩沖在同一事件循環(huán)中發(fā)生的所有數(shù)據(jù)變更,如果同一個watcher
被多次觸發(fā),只會被推入到隊列中一次,這種在緩沖時去除重復(fù)數(shù)據(jù)對于避免不必要的計算和DOM
操作是非常重要的,然后,在下一個的事件循環(huán)tick
中,Vue
刷新隊列并執(zhí)行實際(已去重的)工作,Vue
在內(nèi)部對異步隊列嘗試使用原生的Promise.then
、MutationObserver
和setImmediate
,如果執(zhí)行環(huán)境不支持,則會采用setTimeout(fn, 0)
代替。
對于Vue
為何采用異步渲染,簡單來說就是為了提升性能,因為不采用異步更新,在每次更新數(shù)據(jù)都會對當(dāng)前組件進(jìn)行重新渲染,為了性能考慮,Vue
會在本輪數(shù)據(jù)更新后,再去異步更新視圖,舉個例子,讓我們在一個方法內(nèi)重復(fù)更新一個值。
this.msg = 1;
this.msg = 2;
this.msg = 3;
事實上,我們真正想要的其實只是最后一次更新而已,也就是說前三次DOM
更新都是可以省略的,我們只需要等所有狀態(tài)都修改好了之后再進(jìn)行渲染就可以減少一些性能損耗。
對于渲染方面的問題是很明確的,最終只渲染一次肯定比修改之后即渲染所耗費的性能少,在這里我們還需要考慮一下異步更新隊列的相關(guān)問題,假設(shè)我們現(xiàn)在是進(jìn)行了相關(guān)處理使得每次更新數(shù)據(jù)只進(jìn)行一次真實DOM
渲染,來讓我們考慮異步更新隊列的性能優(yōu)化。
假設(shè)這里是同步更新隊列,this.msg=1
,大致會發(fā)生這些事: msg
值更新 ->
觸發(fā)setter
->
觸發(fā)Watcher
的update
->
重新調(diào)用 render
->
生成新的vdom -> dom-diff -> dom
更新,這里的dom
更新并不是渲染(即布局、繪制、合成等一系列步驟),而是更新內(nèi)存中的DOM
樹結(jié)構(gòu),之后再運行this.msg=2
,再重復(fù)上述步驟,之后的第3
次更新同樣會觸發(fā)相同的流程,等開始渲染的時候,最新的DOM
樹中確實只會存在更新完成3
,從這里來看,前2
次對msg
的操作以及Vue
內(nèi)部對它的處理都是無用的操作,可以進(jìn)行優(yōu)化處理。
如果是異步更新隊列,會是下面的情況,運行this.msg=1
,并不是立即進(jìn)行上面的流程,而是將對msg
有依賴的Watcher
都保存在隊列中,該隊列可能這樣[Watcher1, Watcher2...]
,當(dāng)運行this.msg=2
后,同樣是將對msg
有依賴的Watcher
保存到隊列中,Vue
內(nèi)部會做去重判斷,這次操作后,可以認(rèn)為隊列數(shù)據(jù)沒有發(fā)生變化,第3
次更新也是上面的過程,當(dāng)然,你不可能只對msg
有操作,你可能對該組件中的另一個屬性也有操作,比如this.otherMsg=othermessage
,同樣會把對otherMsg
有依賴的Watcher
添加到異步更新隊列中,因為有重復(fù)判斷操作,這個Watcher
也只會在隊列中存在一次,本次異步任務(wù)執(zhí)行結(jié)束后,會進(jìn)入下一個任務(wù)執(zhí)行流程,其實就是遍歷異步更新隊列中的每一個Watcher
,觸發(fā)其update
,然后進(jìn)行重新調(diào)用render
->
new vdom
->
dom-diff
->
dom
更新等流程,但是這種方式和同步更新隊列相比,不管操作多少次msg
, Vue
在內(nèi)部只會進(jìn)行一次重新調(diào)用真實更新流程,所以,對于異步更新隊列不是節(jié)省了渲染成本,而是節(jié)省了Vue
內(nèi)部計算及DOM
樹操作的成本,不管采用哪種方式,渲染確實只有一次。
此外,組件內(nèi)部實際使用VirtualDOM
進(jìn)行渲染,也就是說,組件內(nèi)部其實是不關(guān)心哪個狀態(tài)發(fā)生了變化,它只需要計算一次就可以得知哪些節(jié)點需要更新,也就是說,如果更改了N
個狀態(tài),其實只需要發(fā)送一個信號就可以將DOM
更新到最新,如果我們更新多個值。
this.msg = 1;
this.age = 2;
this.name = 3;
此處我們分三次修改了三種狀態(tài),但其實Vue
只會渲染一次,因為VIrtualDOM
只需要一次就可以將整個組件的DOM
更新到最新,它根本不會關(guān)心這個更新的信號到底是從哪個具體的狀態(tài)發(fā)出來的。
而為了達(dá)到這個目的,我們需要將渲染操作推遲到所有的狀態(tài)都修改完成,為了做到這一點只需要將渲染操作推遲到本輪事件循環(huán)的最后或者下一輪事件循環(huán),也就是說,只需要在本輪事件循環(huán)的最后,等前面更新狀態(tài)的語句都執(zhí)行完之后,執(zhí)行一次渲染操作,它就可以無視前面各種更新狀態(tài)的語法,無論前面寫了多少條更新狀態(tài)的語句,只在最后渲染一次就可以了。
將渲染推遲到本輪事件循環(huán)的最后執(zhí)行渲染的時機會比推遲到下一輪快很多,所以Vue
優(yōu)先將渲染操作推遲到本輪事件循環(huán)的最后,如果執(zhí)行環(huán)境不支持會降級到下一輪,Vue
的變化偵測機制(setter
)決定了它必然會在每次狀態(tài)發(fā)生變化時都會發(fā)出渲染的信號,但Vue
會在收到信號之后檢查隊列中是否已經(jīng)存在這個任務(wù),保證隊列中不會有重復(fù),如果隊列中不存在則將渲染操作添加到隊列中,之后通過異步的方式延遲執(zhí)行隊列中的所有渲染的操作并清空隊列,當(dāng)同一輪事件循環(huán)中反復(fù)修改狀態(tài)時,并不會反復(fù)向隊列中添加相同的渲染操作,所以我們在使用Vue
時,修改狀態(tài)后更新DOM
都是異步的。
當(dāng)數(shù)據(jù)變化后會調(diào)用notify
方法,將watcher
遍歷,調(diào)用update
方法通知watcher
進(jìn)行更新,這時候watcher
并不會立即去執(zhí)行,在update
中會調(diào)用queueWatcher
方法將watcher
放到了一個隊列里,在queueWatcher
會根據(jù)watcher
的進(jìn)行去重,若多個屬性依賴一個watcher
,則如果隊列中沒有該watcher
就會將該watcher
添加到隊列中,然后便會在$nextTick
方法的執(zhí)行隊列中加入一個flushSchedulerQueue
方法(這個方法將會觸發(fā)在緩沖隊列的所有回調(diào)的執(zhí)行),然后將$nextTick
方法的回調(diào)加入$nextTick
方法中維護(hù)的執(zhí)行隊列,flushSchedulerQueue
中開始會觸發(fā)一個before
的方法,其實就是beforeUpdate
,然后watcher.run
()才開始真正執(zhí)行watcher
,執(zhí)行完頁面就渲染完成,更新完成后會調(diào)用updated
鉤子。
在上文中談到了對于Vue
為何采用異步渲染,假如此時我們有一個需求,需要在頁面渲染完成后取得頁面的DOM
元素,而由于渲染是異步的,我們不能直接在定義的方法中同步取得這個值的,于是就有了vm.$nextTick
方法,Vue
中$nextTick
方法將回調(diào)延遲到下次DOM
更新循環(huán)之后執(zhí)行,也就是在下次DOM
更新循環(huán)結(jié)束之后執(zhí)行延遲回調(diào),在修改數(shù)據(jù)之后立即使用這個方法,能夠獲取更新后的DOM
。簡單來說就是當(dāng)數(shù)據(jù)更新時,在DOM
中渲染完成后,執(zhí)行回調(diào)函數(shù)。
通過一個簡單的例子來演示$nextTick
方法的作用,首先需要知道Vue
在更新DOM
時是異步執(zhí)行的,也就是說在更新數(shù)據(jù)時其不會阻塞代碼的執(zhí)行,直到執(zhí)行棧中代碼執(zhí)行結(jié)束之后,才開始執(zhí)行異步任務(wù)隊列的代碼,所以在數(shù)據(jù)更新時,組件不會立即渲染,此時在獲取到DOM
結(jié)構(gòu)后取得的值依然是舊的值,而在$nextTick
方法中設(shè)定的回調(diào)函數(shù)會在組件渲染完成之后執(zhí)行,取得DOM
結(jié)構(gòu)后取得的值便是新的值。
官方文檔中說明,Vue
在更新DOM
時是異步執(zhí)行的,只要偵聽到數(shù)據(jù)變化,Vue
將開啟一個隊列,并緩沖在同一事件循環(huán)中發(fā)生的所有數(shù)據(jù)變更,如果同一個watcher
被多次觸發(fā),只會被推入到隊列中一次。這種在緩沖時去除重復(fù)數(shù)據(jù)對于避免不必要的計算和DOM
操作是非常重要的。然后,在下一個的事件循環(huán)tick
中,Vue
刷新隊列并執(zhí)行實際工作。Vue
在內(nèi)部對異步隊列嘗試使用原生的Promise.then
、MutationObserver
和setImmediate
,如果執(zhí)行環(huán)境不支持,則會采用 setTimeout(fn, 0)
代替。Js
是單線程的,其引入了同步阻塞與異步非阻塞的執(zhí)行模式,在Js
異步模式中維護(hù)了一個Event Loop
,Event Loop
是一個執(zhí)行模型,在不同的地方有不同的實現(xiàn),瀏覽器和NodeJS
基于不同的技術(shù)實現(xiàn)了各自的Event Loop
。瀏覽器的Event Loop
是在HTML5
的規(guī)范中明確定義,NodeJS
的Event Loop
是基于libuv
實現(xiàn)的。
在瀏覽器中的Event Loop
由執(zhí)行棧Execution Stack
、后臺線程Background Threads
、宏隊列Macrotask Queue
、微隊列Microtask Queue
組成。
執(zhí)行棧就是在主線程執(zhí)行同步任務(wù)的數(shù)據(jù)結(jié)構(gòu),函數(shù)調(diào)用形成了一個由若干幀組成的棧。
后臺線程就是瀏覽器實現(xiàn)對于setTimeout
、setInterval
、XMLHttpRequest
等等的執(zhí)行線程。
宏隊列,一些異步任務(wù)的回調(diào)會依次進(jìn)入宏隊列,等待后續(xù)被調(diào)用,包括setTimeout
、setInterval
、setImmediate(Node)
、requestAnimationFrame
、UI rendering
、I/O
等操作。
微隊列,另一些異步任務(wù)的回調(diào)會依次進(jìn)入微隊列,等待后續(xù)調(diào)用,包括Promise
、process.nextTick(Node)
、Object.observe
、MutationObserver
等操作。
當(dāng)Js
執(zhí)行時,進(jìn)行如下流程:
首先將執(zhí)行棧中代碼同步執(zhí)行,將這些代碼中異步任務(wù)加入后臺線程中。
執(zhí)行棧中的同步代碼執(zhí)行完畢后,執(zhí)行棧清空,并開始掃描微隊列。
取出微隊列隊首任務(wù),放入執(zhí)行棧中執(zhí)行,此時微隊列是進(jìn)行了出隊操作。
當(dāng)執(zhí)行棧執(zhí)行完成后,繼續(xù)出隊微隊列任務(wù)并執(zhí)行,直到微隊列任務(wù)全部執(zhí)行完畢。
最后一個微隊列任務(wù)出隊并進(jìn)入執(zhí)行棧后微隊列中任務(wù)為空,當(dāng)執(zhí)行棧任務(wù)完成后,開始掃面微隊列為空,繼續(xù)掃描宏隊列任務(wù),宏隊列出隊,放入執(zhí)行棧中執(zhí)行,執(zhí)行完畢后繼續(xù)掃描微隊列為空則掃描宏隊列,出隊執(zhí)行。
不斷往復(fù)...
。
// Step 1
console.log(1);
// Step 2
setTimeout(() => {
console.log(2);
Promise.resolve().then(() => {
console.log(3);
});
}, 0);
// Step 3
new Promise((resolve, reject) => {
console.log(4);
resolve();
}).then(() => {
console.log(5);
})
// Step 4
setTimeout(() => {
console.log(6);
}, 0);
// Step 5
console.log(7);
// Step N
// ...
// Result
/*
1
4
7
5
2
3
6
*/
// 執(zhí)行棧 console
// 微隊列 []
// 宏隊列 []
console.log(1); // 1
// 執(zhí)行棧 setTimeout
// 微隊列 []
// 宏隊列 [setTimeout1]
setTimeout(() => {
console.log(2);
Promise.resolve().then(() => {
console.log(3);
});
}, 0);
// 執(zhí)行棧 Promise
// 微隊列 [then1]
// 宏隊列 [setTimeout1]
new Promise((resolve, reject) => {
console.log(4); // 4 // Promise是個函數(shù)對象,此處是同步執(zhí)行的 // 執(zhí)行棧 Promise console
resolve();
}).then(() => {
console.log(5);
})
// 執(zhí)行棧 setTimeout
// 微隊列 [then1]
// 宏隊列 [setTimeout1 setTimeout2]
setTimeout(() => {
console.log(6);
}, 0);
// 執(zhí)行棧 console
// 微隊列 [then1]
// 宏隊列 [setTimeout1 setTimeout2]
console.log(7); // 7
// 執(zhí)行棧 then1
// 微隊列 []
// 宏隊列 [setTimeout1 setTimeout2]
console.log(5); // 5
// 執(zhí)行棧 setTimeout1
// 微隊列 [then2]
// 宏隊列 [setTimeout2]
console.log(2); // 2
Promise.resolve().then(() => {
console.log(3);
});
// 執(zhí)行棧 then2
// 微隊列 []
// 宏隊列 [setTimeout2]
console.log(3); // 3
// 執(zhí)行棧 setTimeout2
// 微隊列 []
// 宏隊列 []
console.log(6); // 6
在了解異步任務(wù)的執(zhí)行隊列后,回到中$nextTick
方法,當(dāng)用戶數(shù)據(jù)更新時,Vue
將會維護(hù)一個緩沖隊列,對于所有的更新數(shù)據(jù)將要進(jìn)行的組件渲染與DOM
操作進(jìn)行一定的策略處理后加入緩沖隊列,然后便會在$nextTick
方法的執(zhí)行隊列中加入一個flushSchedulerQueue
方法(這個方法將會觸發(fā)在緩沖隊列的所有回調(diào)的執(zhí)行),然后將$nextTick
方法的回調(diào)加入$nextTick
方法中維護(hù)的執(zhí)行隊列,在異步掛載的執(zhí)行隊列觸發(fā)時就會首先會首先執(zhí)行flushSchedulerQueue
方法來處理DOM
渲染的任務(wù),然后再去執(zhí)行$nextTick
方法構(gòu)建的任務(wù),這樣就可以實現(xiàn)在$nextTick
方法中取得已渲染完成的DOM
結(jié)構(gòu)。在測試的過程中發(fā)現(xiàn)了一個很有意思的現(xiàn)象,在上述例子中的加入兩個按鈕,在點擊updateMsg
按鈕的結(jié)果是3 2 1
,點擊updateMsgTest
按鈕的運行結(jié)果是2 3 1
。
這里假設(shè)運行環(huán)境中Promise
對象是完全支持的,那么使用setTimeout
是宏隊列在最后執(zhí)行這個是沒有異議的,但是使用$nextTick
方法以及自行定義的Promise
實例是有執(zhí)行順序的問題的,雖然都是微隊列任務(wù),但是在Vue
中具體實現(xiàn)的原因?qū)е铝藞?zhí)行順序可能會有所不同,首先直接看一下$nextTick
方法的源碼,關(guān)鍵地方添加了注釋,請注意這是Vue2.4.2
版本的源碼,在后期$nextTick
方法可能有所變更。
/**
* Defer a task to execute it asynchronously.
*/
var nextTick = (function () {
// 閉包 內(nèi)部變量
var callbacks = []; // 執(zhí)行隊列
var pending = false; // 標(biāo)識,用以判斷在某個事件循環(huán)中是否為第一次加入,第一次加入的時候才觸發(fā)異步執(zhí)行的隊列掛載
var timerFunc; // 以何種方法執(zhí)行掛載異步執(zhí)行隊列,這里假設(shè)Promise是完全支持的
function nextTickHandler () { // 異步掛載的執(zhí)行任務(wù),觸發(fā)時就已經(jīng)正式準(zhǔn)備開始執(zhí)行異步任務(wù)了
pending = false; // 標(biāo)識置false
var copies = callbacks.slice(0); // 創(chuàng)建副本
callbacks.length = 0; // 執(zhí)行隊列置空
for (var i = 0; i < copies.length; i++) {
copies[i](); // 執(zhí)行
}
}
// the nextTick behavior leverages the microtask queue, which can be accessed
// via either native Promise.then or MutationObserver.
// MutationObserver has wider support, however it is seriously bugged in
// UIWebView in iOS >= 9.3.3 when triggered in touch event handlers. It
// completely stops working after triggering a few times... so, if native
// Promise is available, we will use it:
/* istanbul ignore if */
if (typeof Promise !== 'undefined' && isNative(Promise)) {
var p = Promise.resolve();
var logError = function (err) { console.error(err); };
timerFunc = function () {
p.then(nextTickHandler).catch(logError); // 掛載異步任務(wù)隊列
// in problematic UIWebViews, Promise.then doesn't completely break, but
// it can get stuck in a weird state where callbacks are pushed into the
// microtask queue but the queue isn't being flushed, until the browser
// needs to do some other work, e.g. handle a timer. Therefore we can
// "force" the microtask queue to be flushed by adding an empty timer.
if (isIOS) { setTimeout(noop); }
};
} else if (typeof MutationObserver !== 'undefined' && (
isNative(MutationObserver) ||
// PhantomJS and iOS 7.x
MutationObserver.toString() === '[object MutationObserverConstructor]'
)) {
// use MutationObserver where native Promise is not available,
// e.g. PhantomJS IE11, iOS7, Android 4.4
var counter = 1;
var observer = new MutationObserver(nextTickHandler);
var textNode = document.createTextNode(String(counter));
observer.observe(textNode, {
characterData: true
});
timerFunc = function () {
counter = (counter + 1) % 2;
textNode.data = String(counter);
};
} else {
// fallback to setTimeout
/* istanbul ignore next */
timerFunc = function () {
setTimeout(nextTickHandler, 0);
};
}
return function queueNextTick (cb, ctx) { // nextTick方法真正導(dǎo)出的方法
var _resolve;
callbacks.push(function () { // 添加到執(zhí)行隊列中 并加入異常處理
if (cb) {
try {
cb.call(ctx);
} catch (e) {
handleError(e, ctx, 'nextTick');
}
} else if (_resolve) {
_resolve(ctx);
}
});
//判斷在當(dāng)前事件循環(huán)中是否為第一次加入,若是第一次加入則置標(biāo)識為true并執(zhí)行timerFunc函數(shù)用以掛載執(zhí)行隊列到Promise
// 這個標(biāo)識在執(zhí)行隊列中的任務(wù)將要執(zhí)行時便置為false并創(chuàng)建執(zhí)行隊列的副本去運行執(zhí)行隊列中的任務(wù),參見nextTickHandler函數(shù)的實現(xiàn)
// 在當(dāng)前事件循環(huán)中置標(biāo)識true并掛載,然后再次調(diào)用nextTick方法時只是將任務(wù)加入到執(zhí)行隊列中,直到掛載的異步任務(wù)觸發(fā),便置標(biāo)識為false然后執(zhí)行任務(wù),再次調(diào)用nextTick方法時就是同樣的執(zhí)行方式然后不斷如此往復(fù)
if (!pending) {
pending = true;
timerFunc();
}
if (!cb && typeof Promise !== 'undefined') {
return new Promise(function (resolve, reject) {
_resolve = resolve;
})
}
}
})();
回到剛才提出的問題上,在更新DOM
操作時會先觸發(fā)$nextTick
方法的回調(diào),解決這個問題的關(guān)鍵在于誰先將異步任務(wù)掛載到Promise
對象上。
首先對有數(shù)據(jù)更新的updateMsg
按鈕觸發(fā)的方法進(jìn)行debug
,斷點設(shè)置在Vue.js
的715
行,版本為2.4.2
,在查看調(diào)用棧以及傳入的參數(shù)時可以觀察到第一次執(zhí)行$nextTick
方法的其實是由于數(shù)據(jù)更新而調(diào)用的nextTick(flushSchedulerQueue);
語句,也就是說在執(zhí)行this.msg = "Update";
的時候就已經(jīng)觸發(fā)了第一次的$nextTick
方法,此時在$nextTick
方法中的任務(wù)隊列會首先將flushSchedulerQueue
方法加入隊列并掛載$nextTick
方法的執(zhí)行隊列到Promise
對象上,然后才是自行自定義的Promise.resolve().then(() => console.log(2))
語句的掛載,當(dāng)執(zhí)行微任務(wù)隊列中的任務(wù)時,首先會執(zhí)行第一個掛載到Promise
的任務(wù),此時這個任務(wù)是運行執(zhí)行隊列,這個隊列中有兩個方法,首先會運行flushSchedulerQueue
方法去觸發(fā)組件的DOM
渲染操作,然后再執(zhí)行console.log(3)
,然后執(zhí)行第二個微隊列的任務(wù)也就是() => console.log(2)
,此時微任務(wù)隊列清空,然后再去宏任務(wù)隊列執(zhí)行console.log(1)
。
接下來對于沒有數(shù)據(jù)更新的updateMsgTest
按鈕觸發(fā)的方法進(jìn)行debug
,斷點設(shè)置在同樣的位置,此時沒有數(shù)據(jù)更新,那么第一次觸發(fā)$nextTick
方法的是自行定義的回調(diào)函數(shù),那么此時$nextTick
方法的執(zhí)行隊列才會被掛載到Promise
對象上,很顯然在此之前自行定義的輸出2
的Promise
回調(diào)已經(jīng)被掛載,那么對于這個按鈕綁定的方法的執(zhí)行流程便是首先執(zhí)行console.log(2)
,然后執(zhí)行$nextTick
方法閉包的執(zhí)行隊列,此時執(zhí)行隊列中只有一個回調(diào)函數(shù)console.log(3)
,此時微任務(wù)隊列清空,然后再去宏任務(wù)隊列執(zhí)行console.log(1)
。
簡單來說就是誰先掛載Promise
對象的問題,在調(diào)用$nextTick
方法時就會將其閉包內(nèi)部維護(hù)的執(zhí)行隊列掛載到Promise
對象,在數(shù)據(jù)更新時Vue
內(nèi)部首先就會執(zhí)行$nextTick
方法,之后便將執(zhí)行隊列掛載到了Promise
對象上,其實在明白Js
的Event Loop
模型后,將數(shù)據(jù)更新也看做一個$nextTick
方法的調(diào)用,并且明白$nextTick
方法會一次性執(zhí)行所有推入的回調(diào),就可以明白其執(zhí)行順序的問題了,下面是一個關(guān)于$nextTick
方法的最小化的DEMO
。
var nextTick = (function(){
var pending = false;
const callback = [];
var p = Promise.resolve();
var handler = function(){
pending = true;
callback.forEach(fn => fn());
}
var timerFunc = function(){
p.then(handler);
}
return function queueNextTick(fn){
callback.push(() => fn());
if(!pending){
pending = true;
timerFunc();
}
}
})();
(function(){
nextTick(() => console.log("觸發(fā)DOM渲染隊列的方法")); // 注釋 / 取消注釋 來查看效果
setTimeout(() => console.log(1))
Promise.resolve().then(() => console.log(2))
nextTick(() => {
console.log(3)
})
})();
關(guān)于“vue頁面渲染是異步的嗎”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識,可以關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,小編每天都會為大家更新不同的知識點。