[VueJS] 重新檢視 lifecycle 與 vue-router

由於遇到了一些 Google reCAPTCHA 的狀況,所以剛好找時間來重新檢視一下 Vue 元件的生命週期這件事。其實也不是 Vue 本身的問題,但是很奇妙的是,我真的就是遇到這種狀況。

剛好複習一下也好。


Lifecycle

首先還是先複習一下官方這張說明圖片,

lifecycle

https://vuejs.org/v2/guide/instance.html#Lifecycle-Diagram

接著回想一下我上次寫過,關於 Vue-Router 外面的那些事情。

然後我們來玩一下排列組合,在這邊只關注 剛進去 元件或是路由的情況,

  • beforeRouteEnter
  • beforeCreate
  • created
  • beforeMount
  • mounted

再加上,原先 beforeRouteEnter 當中有 next() 可以使用,所以你會有這種用法,

beforeRouteEnter (to, from, next) {
    next(vm => {
        // 我們在這邊也算他是 Lifecycle 的一環
        // 先簡稱他叫做 nextInVM
    })
}

所以我們先假設他叫做 nextInVM 好了,那麼,這些東西實際的執行順序會是什麼呢?

  1. beforeRouteEnter
  2. beforeCreate
  3. created
  4. beforeMount
  5. mounted
  6. nextInVM

元件內的執行順序

只有單一元件的時候,執行結果確實會向上述那樣。假設,你包含了其他元件,舉例來說,

App
  - AComponent
  - BComponent
    - CComponent
    - DComponent

那麼順序會是如何呢?首先,要先看你的 HTML 標籤順序 ,他會先決定元件的載入順序(大多數情況來說會依照此順序),如果上述的元件是這樣,

<app>
    <a-component></a-component>
    <b-component>
        <c-component></c-component>
        <d-component></d-component>
    </b-component>
</app>

那麼你覺得在上述的載入執行順序會是如何?

  1. beforeRouteEnter 從 App 發出
  2. beforeCreate 從 App 發出
  3. created 從 App 發出
  4. beforeMount 從 App 發出
  5. beforeCreate 從 A Component 發出
  6. created 從 A Component 發出
  7. beforeMount 從 A Component 發出
  8. beforeCreate 從 B Component 發出
  9. created 從 B Component 發出
  10. beforeMount 從 B Component 發出
  11. beforeCreate 從 C Component 發出
  12. created 從 C Component 發出
  13. beforeMount 從 C Component 發出
  14. beforeCreate 從 D Component 發出
  15. created 從 D Component 發出
  16. beforeMount 從 D Component 發出
  17. mounted 從 A Component 發出
  18. mounted 從 C Component 發出
  19. mounted 從 D Component 發出
  20. mounted 從 B Component 發出
  21. mounted 從 App 發出
  22. nextInVM 從 App 發出

還記得 beforeRouteEnter 這個東西,沒有在 Router 裡面不會觸發吧?所以 nextInVM 這件事情只會存在於 App 裡面而已。

雷點

有些外掛套件,或是有時候我們想自己注入一點其他的東西,那麼注入的 時機點 就變得異常的敏感。如果以為 created 或是 mounted 都是萬解的話,請看一下上面提到的順序,如果 D Component 要用的東西,由 B Component 去載入,即便順序提前,他還是有機會找不到東西的。

所以諸如 Google reCAPTCHA 的 API,他還是建議你寫在 index.html 裡面,像我自己動態載入就會出現 reCAPTCHA API 找不到的情況。

爾或者是,當你注入的東西是屬於非同步傳輸,那麼就更有機會找不到當初注入的東西。所以,有這一些相關的注入,可能還是得使用 Events 或是在外面做一個 EventBus 來解決,起碼有個 onLoad 事件能夠讓你知道注入的東西已經讀取完畢。

接著,next() 會讓人覺得是路由進入之後,第一個會跑的東西,但是,請回頭看一下上述的順序。

天殺的他是最後才執行!

所以,有些運算的資料邏輯,放在這裡面,是有機會造成其他子元件的異常的。如果有搭配好 Vuex 或許可以解決部分狀況,不過,還是建議兒子的事情交由兒子自己去做,不要逢年過節就問什麼時候要生孩子之類的問題。

至於我踩到的雷點,是因為外掛用了 defer(),他先註冊一個 defer() 來等待 Google reCAPTCHA 載入成功,之後再去初始化。問題來了,如果我將 Google reCAPTCHA 的 API 動態載入,那麼,思考一下這個狀況,

  1. 到底是 reCAPTCHA API 先載入?
  2. 還是外掛先掛入 defer() 來等待 resolve
  3. 當中會判斷 window.hasOwnProperty('grecaptcha') 是否為真

理論上來說,應該是,

  1. 先掛入 defer() 來等 resolve
  2. 載入 Google reCAPTCHA API 使用 onload 參數回戳外掛初始化
  3. 外掛收到 API 回呼後 resolve
  4. 啟動 Google reCAPTCHA 功能

然後,

我起不來!

然後套件的 defer() 是錯的!

但是我懶得吐嘈了。

小結

套件還是自己寫比較靠譜,重造輪子偶爾還是有點用處的(選我正解。

Hina Chen
偏執與強迫症的患者,算不上是無可救藥,只是我已經遇上我的良醫了。
Taipei