微前端框架single-spa子應用載入解析

2023-03-28 18:01:45

作者:京東物流 寧衝

1 前言

什麼是微前端?
微前端是指存在於瀏覽器中的微服務。

本文主要通過對微前端框架single-spa的基座應用載入子應用的single-spa-vue函數庫進行分析,通過程式碼維度分析讓大家瞭解在single-spa載入子應用的時候都做了哪些事情。如何通過優化single-spa-vue函數庫保持子應用的狀態。

由於是在程式碼維度進行分析,要求讀者對single-spa有一定的瞭解,閱讀效果會更好。

2 single-spa載入子應用的過程

基座應用中設定的載入子應用設定,在設定子應用物件的app方法中會把子應用chunk-vendors.js、app.js插入到頁面,並且執行chunk-vendors.js、app.js兩個子應用依賴的js。

app方法執行結束以後,子應用依賴的js插入頁面以後的dom結構。

由上面app方法的執行過程,我們可以看出基座應用載入子應用,和vue框架打包後載入js資源的方式類似。那麼為什麼只需要載入子應用的app.js和chunk-vendors.js就可以把子應用渲染到頁面上面。下面我們再來看看子應用的入口檔案main.js方法中是如何進行設定的。

3 子應用的main.js中的設定

在子應用main.js中我們可以看到,把常規的設定1替換為設定2的格式。

在設定2中我們會把當前vue的設定傳給single-spa-vue函數庫中。包括el、render方法、Vue物件。通過singleSpaVue的包裝,返回single-spa生命週期方法bootstrap、mount、unmount。

  • bootstrap:引導函數,應用內容首次掛載到頁面前呼叫,只會執行一次。
  • mount:掛載函數,子應用每次被掛載的時候都會執行。
  • unmount:解除安裝函數,子應用每次被解除安裝的時候都會執行。

也就是說基座應用在載入子應用的時候就是通過single-spa-vue函數的處理,生成了single-spa的各個生命週期,給基座應用在載入子應用的時候呼叫。
下面我們來看看single-spa-vue究竟是如何生成各種生命週期函數的。

4 single-spa-vue原始碼結構

single-spa-vue函數庫可以幫助我們來生成載入子應用的生命週期。你可以通過下面連結:single-spa-vue GIT地址,下載single-spa-vue函數庫的原始碼。
single-spa-vue的原始碼非常簡單,只有幾個生成single-spa生命週期的函數。

  • single-spa-vue方法:single-spa-vue函數庫對外提供服務的唯一一個方法,用來接收設定項,並且返回一個包含各個single-spa生命週期的物件。
  • single-spa-vue中的其他方法bootstrap、mount、update、unmount是用來生成single-spa對應生命週期函數的方法。

下面我們詳細介紹一下這幾個方法的作用和實現。

5 single-spa-vue原始碼解析

single-spa-vue中提供的singleSpaVue方法會接收userOpts設定資訊,設定資訊會和預設的設定項defaultOpts合併以後再進行進行後續處理。

5.1 設定項defaultOpts

對於預設的設定項會有下面這幾項,這裡只介紹一下必填項,appOptions、Vue/createApp。其中的template在並沒有在single-spa-vue中被用到,但是在single-spa-html中有使用到。

  1. // 預設設定項列表

  2. const defaultOpts = {

  3. // required opts

  4. appOptions: null,

  5. template: null,

  6. // sometimes require opts

  7. Vue: null,

  8. createApp: null,

  9. handleInstance: null

  10. }

1)appOptions設定項介紹

  • appOptions:應用的設定項,會在初始化Vue範例的時候作為引數傳給Vue方法,下面具體介紹一下appOptions中的設定項。
  • el:子應用需要掛載的基座dom,即vue需要掛載的dom。
  • render/template:vue的render/template設定項。
  • data:初始化的引數物件,會在執行掛載函數mount的時候直接掛載到vue範例上。

2)Vue/createApp設定項介紹

Vue/createApp設定項是用來生成vue範例的,single-spa-vue函數庫可以通過傳入的Vue物件在mount方法中來生成vue範例。也可以傳入createApp方法,由子應用在createApp方法中返回vue範例。

下面我們會在生命週期生成方法中看到這些設定項的作用

5.2 入口方法:singleSpaVue

singleSpaVue方法會對入參先進行下列有效性校驗,具體校驗的內容有一下四項。

  1. 設定項userOpts是否為物件。
  2. 用來建立vue範例的設定Vue/createApp是否存在。
  3. 用來生成vue範例的設定項appOptions是否存在。
  4. vue範例掛載的dom是否正確appOptions.el有效性校驗。

有效性校驗通過以後,會呼叫bootstrap、mount、unmount、update方法用來分別生成single-spa載入子應用的生命週期函數。
下面詳細介紹一下各個生命週期函數是如何生成的。

5.3 引導函數:bootstrap

引導函數bootstrap,當應用內容首次掛載到頁面前呼叫。

bootstrap函數,會先判斷一下是否在設定項中存在loadRootComponent。設定項loadRootComponent筆者理解是可以用來載入當前子應用依賴的父級應用或者其他的依賴資源。
比如A頁面依賴B元件,那在載入A頁面的時候可以loadRootComponent方法中把B元件的靜態資源載入下來,並且渲染到頁面上,A頁面就可以直接使用B元件中提供的一些資源或者dom。
如果未設定loadRootComponent,則直接返回,不做任何處理。

5.4 子應用掛載生命週期:mount

在子應用每次被掛載到頁面上的時候,single-spa會執行mount生命週期函數,single-spa-vue在mount函數中用來初始化子應用的vue範例,並且把範例掛載到頁面上。
mount函數會接收三個引數,分別是singleSpaVue傳入的引數opts,single-spa-vue當前全域性掛載的子應用範例列表mountedInstances,在基座應用中註冊的當前子應用的single-spa範例props
props入參接收的資料,這裡只用到了props的name屬性,用來標識當前掛載的子應用。

在mount方法中主要做了以下幾件事

1)格式化應用設定項appOptions

mount方法中會通過resolveAppOptions方法來格式化應用的設定項appOptions。
在resolveAppOptions方法來中如果appOptions是一個方法的話,則執行方法,並且傳遞基座應用通過customProps引數傳入的引數props。
子應用可以在appOptions方法中獲取父應用傳遞的引數、根據父應用的狀態做不同的處理等。

2)初始化子應用需要掛載的DOM物件。

初始化dom的設定和方法比較多,會從很多個設定項中來獲取el,程式碼比較簡單,這裡就不贅述了。只列出來取值的優先順序供大家參考。
appOptions.el > props.domElement > props.name

dom節點被格式化完成以後,下面就開始把頁面掛載到dom上面。

3)建立vue範例,並且掛載到頁面上

在掛載頁面之前會有一個設定項:replaceMode,replaceMode設定項是用來標識是否需要替換el節點下的內容。預設replaceMode為false是會在el節點下面新建一個class為single-spa-container的空白div來儲存子應用。

以demo中的程式碼為例:

預設replaceMode為false的情況下,基座中的的內容和vue1中的內容會並存。如下圖:

如果把replaceMode改為true的情況下,會發現基座中的內容會被vue1中的內容覆蓋掉。
如下圖:

設定掛載方式以後,就開始把子應用真正掛載到dom上面。
這裡支援兩種掛載方式,一種是使用vue3的createApp方式掛載,一種是使用vue2的new Vue方式進行掛載。這裡的掛載方式其實和我們在使用vue掛載到dom方式的設定是一樣的。

通過上面的步驟,此時子應用就被掛載到了html的頁面中的dom上了。

5.5 更新生命週期函數:update

當呼叫parcel.update()會觸發update方法,筆者由於並沒有用的高階特性parcel,在此處不再做過多介紹。

5.6 子應用解除安裝生命週期:unmount

當頁面url發生變化的時候,離開子應用頁面路由的時候會觸發unmount方法。
在unmount方法中主要做的事情有:

  1. 解除安裝vue應用
  2. 刪除vue範例
  3. 同時清空頁面dom。

執行unmount操作以後子應用的資料和dom就會被清除乾淨。

5.7 小結

以上就是single-spa官方提供的single-spa-vue函數庫來掛載子應用的全部流程。通過上面的分析,我們發現single-spa-vue函數庫會通過呼叫singleSpaVue方法返回一個包含bootstrap、mount、update、unmount四個方法的物件。在基座應用載入子應用的生命週期會執行對應的方法,通過執行方法把子應用掛載到基座應用或者從基座應用銷燬子應用。

通過single-spa-vue中載入子應用的過程,single-spa把各個子應用組裝成一個複雜的應用,在使用者無感的情況下對各個子應用進行分別治理。

single-spa在載入子應用的時候,除了vue的版本之外,還有其他的版本,但是載入的思路類似,都是在函數庫中來把子應用載入到html中,此處不再介紹其他型別的載入。
single-spa其他載入子應用方式:single-spa-react、single-spa-html

6 子應用狀態保持

在實際開發的過程中筆者遇到有些頁面需要使用vue中的keep-alive來保留狀態,在頁面切換的過程中雖然頁面被銷燬但是頁面狀態需要在保留,但是我們在分析single-spa-vue的unmount方法實現的時候發現,如果頁面切換,子應用和vue範例被銷燬,此時子應用中的keep-alive是沒有生效的。

如下圖:

當在input中輸入內容以後,傳統的spa專案可以通過keep-alive來記錄狀態,當頁面路由切換以後,再切回來,仍然保持狀態。但是如果直接使用single-spa-vue,當子應用觸發unmount的時候input中的輸入內容1111會被清空。針對這種情況,筆者對single-spa-vue類庫進行了一些改造。

改造內容如下:

6.1 子應用由銷燬改為隱藏

在single-spa-vue設定項中增加了一個設定項,根據設定項中是否存在isKeepAlive設定項,來判斷把當前的dom隱藏掉,還是刪除。這樣當子應用unmount方法觸發的時候,子應用並未被刪除,而是仍然保留。

同時筆者對vue的route設定也進行了一些優化,當頁面不存在的時候,此時會把子應用中的一個空元件掛載到dom上面,避免雖然頁面被隱藏掉,但是dom仍然在html中,導致頁面dom過多。

6.2 子應用由新建改為顯示

在single-spa-vue設定項中呼叫mount方法掛載子應用的時候,會判斷當前子應用是否存在,如果子應用存在則直接把子頁面顯示出來,由於子應用並未被銷燬,此時子應用中的keep-alive就會一直生效,並且儲存頁面的狀態。

通過對single-spa-vue類庫的mount、unmount方法的優化,使用者在使用頁面的時候真正和使用vue那樣流暢,並且可以保持頁面狀態,提升使用者的體驗。