Vue組件嵌套時(shí)生命周期函數(shù)觸發(fā)順序是什么?

使用過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)鍵階段:

  1. beforeCreate:在實(shí)例初始化之后,數(shù)據(jù)觀測(cè) (data observer) 和 event/watcher 事件配置之前被調(diào)用。
  2. created:在實(shí)例創(chuàng)建完成后被立即調(diào)用。在這一步,實(shí)例已完成數(shù)據(jù)觀測(cè) (data observer),屬性和方法的運(yùn)算,watch/event 事件回調(diào)。
  3. beforeMount:在掛載開始之前被調(diào)用:相關(guān)的 render 函數(shù)首次被調(diào)用。
  4. mounted:實(shí)例被掛載后調(diào)用,這時(shí) el 被新創(chuàng)建的 vm.$el 替換了。
  5. beforeUpdate:數(shù)據(jù)更新時(shí)調(diào)用,發(fā)生在虛擬DOM打補(bǔ)丁之前。
  6. updated:虛擬 DOM 重新渲染和打補(bǔ)丁之后。
  7. beforeDestory:實(shí)例銷毀之前調(diào)用。在這一步,實(shí)例仍然完全可用。
  8. destoryed:實(shí)例銷毀后調(diào)用。對(duì)應(yīng) Vue 實(shí)例的所有指令都被解綁,所有的事件監(jiān)聽器被移除,所有的子實(shí)例也都被銷毀。

我們也可以再看一下 官網(wǎng)的生命周期圖示回想一下。

來源:https://cn.vuejs.org/

針對(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),基本可以確定子組件的更新過程是在父組件的beforeUpdateupdated之間。

修改頁面中的父組件的名稱,可以看到輸出的生命周期觸發(fā)順序確實(shí)如預(yù)期,如下:

3. 銷毀階段

說到這里,銷毀階段應(yīng)該沒啥好說的了,子組件的銷毀是在父組件的beforeDestroydestroyed之間完成的。

點(diǎn)擊示例代碼,取消勾選顯示組件,可以看到如下順序:

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

組件嵌套時(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ā)生在父組件的beforeUpdateupdated之間。

我們稍微翻一下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í)行順序是什么。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容