使用過Vue的大家,對(duì)于生命周期一定都很熟悉,在官方文檔一開始,就給我們介紹了Vue的生命周期有哪些,是怎么樣的順序。這個(gè)難不倒大家。
但如果是問當(dāng)組件嵌套時(shí),父子組件的生命周期函數(shù)觸發(fā)的順序是什么樣的?你是不是會(huì)有一絲絲不確定呢?
如果有的話,就讓我們一起動(dòng)動(dòng)手來確認(rèn)下這個(gè)簡(jiǎn)單的問題吧。
首先,一個(gè)Vue實(shí)例/組件的生命周期中的8個(gè)關(guān)鍵階段:
-
beforeCreate:在實(shí)例初始化之后,數(shù)據(jù)觀測(cè) (data observer) 和 event/watcher 事件配置之前被調(diào)用。 -
created:在實(shí)例創(chuàng)建完成后被立即調(diào)用。在這一步,實(shí)例已完成數(shù)據(jù)觀測(cè) (data observer),屬性和方法的運(yùn)算,watch/event 事件回調(diào)。 -
beforeMount:在掛載開始之前被調(diào)用:相關(guān)的render函數(shù)首次被調(diào)用。 -
mounted:實(shí)例被掛載后調(diào)用,這時(shí)el被新創(chuàng)建的vm.$el替換了。 -
beforeUpdate:數(shù)據(jù)更新時(shí)調(diào)用,發(fā)生在虛擬DOM打補(bǔ)丁之前。 -
updated:虛擬 DOM 重新渲染和打補(bǔ)丁之后。 -
beforeDestory:實(shí)例銷毀之前調(diào)用。在這一步,實(shí)例仍然完全可用。 -
destoryed:實(shí)例銷毀后調(diào)用。對(duì)應(yīng) Vue 實(shí)例的所有指令都被解綁,所有的事件監(jiān)聽器被移除,所有的子實(shí)例也都被銷毀。
我們也可以再看一下 官網(wǎng)的生命周期圖示回想一下。

針對(duì)上面的8個(gè)生命周期,我們可以將其分為三個(gè)階段,分別為:創(chuàng)建掛載階段、更新階段和銷毀階段。
下面就讓我們依次來確認(rèn)下當(dāng)組件嵌套時(shí),這三個(gè)階段生命周期的觸發(fā)順序是怎么樣的?
示例代碼:https://codesandbox.io/s/qiantaozujianshengmingzhouqi-7e5q7
1. 創(chuàng)建掛載階段
如果你仔細(xì)閱讀各階段的描述,你應(yīng)該能想到當(dāng)組件嵌套時(shí),子組件的創(chuàng)建掛載是在父組件掛載的時(shí)候才觸發(fā)的。下面我們來確認(rèn)下。
打開示例代碼,默認(rèn)情況下是沒有渲染組件的。這個(gè)時(shí)候我們點(diǎn)擊:勾選顯示組件,你會(huì)看到如下頁面,父子組件會(huì)渲染出來。

此時(shí),可以看到console中輸出了父子組件的觸發(fā)順序。
順序如下:

可以看到子組件的創(chuàng)建掛載階段確實(shí)是在父組件的掛載階段完成的,開始于父組件的beforeMount之后,結(jié)束于父組件的mounted之前。
2. 更新階段
同樣的,更新階段比較簡(jiǎn)單,有了上面的經(jīng)驗(yàn),基本可以確定子組件的更新過程是在父組件的beforeUpdate和updated之間。
修改頁面中的父組件的名稱,可以看到輸出的生命周期觸發(fā)順序確實(shí)如預(yù)期,如下:

3. 銷毀階段
說到這里,銷毀階段應(yīng)該沒啥好說的了,子組件的銷毀是在父組件的beforeDestroy和destroyed之間完成的。
點(diǎn)擊示例代碼,取消勾選顯示組件,可以看到如下順序:

現(xiàn)在讓我們?cè)诠俜降纳芷趫D示上做一點(diǎn)拓展,加上組件嵌套時(shí)的生命周期。如下圖所示:

好了,今天要分享的內(nèi)容到這里就結(jié)束了。
哈哈,開個(gè)玩笑,顯然不會(huì)這么水,不然歪馬自己都看不下去。
下面我們繼續(xù)。上面我們通過簡(jiǎn)單直觀的方式確認(rèn)了下組件嵌套時(shí),生命周期函數(shù)觸發(fā)的順序是什么樣的。然而縝密的你可能已經(jīng)發(fā)現(xiàn)了,上面的示例都是以同步組件為例的。當(dāng)組件為異步組件時(shí)會(huì)發(fā)生什么變化呢?
3. 當(dāng)組件是異步組件時(shí)
前面,歪馬留了一手,官方文檔上有指出如下內(nèi)容:“mounted 不會(huì)保證所有的子組件也都一起被掛載”、“updated 不會(huì)保證所有的子組件也都一起被重繪?!?。
之所以官網(wǎng)會(huì)給出如此說明,是因?yàn)楫?dāng)組件為異步組件時(shí),生命周期的觸發(fā)順序會(huì)和上面多有不同。
異步子組件的創(chuàng)建和掛載
話不多說,我們先把組件改成異步的,看看結(jié)果。從demo中找到src/components/OuterBox.vue,將InnerBox改為異步加載。如:
// 異步組件
InnerBox: () => import("./InnerBox")
然后我們重新勾選顯示頁面,可以發(fā)現(xiàn),當(dāng)子組件為異步,子組件的創(chuàng)建掛載階段發(fā)生在父組件的beforeUpdate和updated之間。

我們稍微翻一下Vue的源碼,可以看到當(dāng)組件是異步組件時(shí),會(huì)執(zhí)行異步組件的工廠函數(shù),在組件加載完成之后,會(huì)強(qiáng)制更新所有包含該組件的父組件。
異步函數(shù)的工廠函數(shù)就是上面的
() => import("./InnerBox"),官方文檔指出,只要是一個(gè)返回值是Promise的函數(shù)就行。返回值也可以是更復(fù)雜的帶有加載狀態(tài)的對(duì)象,可以參見文檔。
// ...
var forceRender = function (renderCompleted) {
for (var i = 0, l = owners.length; i < l; i++) {
// 依次強(qiáng)制更新父組件
(owners[i]).$forceUpdate();
}
};
var resolve = once(function (res) {
factory.resolved = ensureCtor(res, baseCtor);
// 如果是異步組件,則在resolve時(shí)強(qiáng)制更新父組件。
if (!sync) {
forceRender(true);
} else {
owners.length = 0;
}
});
var reject = once(function (reason) {//...});
// 執(zhí)行異步組件的工廠函數(shù)
var res = factory(resolve, reject);
父組件更新時(shí)同理,如果存在新的異步加載組件,則不會(huì)等待。
好了,今天要分享的內(nèi)容就是這么簡(jiǎn)單,就是想動(dòng)動(dòng)手確認(rèn)下組件嵌套時(shí),父子組件生命周期的執(zhí)行順序是什么。