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

京東雲開發者發表於2023-03-28

作者:京東物流 寧衝

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那樣流暢,並且可以保持頁面狀態,提升使用者的體驗。

相關文章