微前端實踐思考與總結

本文作者系 360 奇舞團前端開發工程師

業務線大多老舊項目在開發體驗以及用戶體驗上都有很多瓶頸,加之項目需要聚合後由內轉外等需求,決定使用微前端方案對各項目進行改造。

簡單介紹下微前端

微前端是一種多個團隊通過獨立發佈功能的方式來共同構建現代化 web 應用的技術手段及方法策略。“類似 Iframe,卻沒有它的各種問題。” 微前端最核心的價值或者特性,就是技術棧無關。通常我們在構建 web 應用時,會有同一框架、同一大版本的限制。微前端的這個特性就打破了傳統 web 的限制。我們利用這個特性,做到 應用拆分聚合、增量升級。再結合實際的場景,來解決問題。

比如,應用拆分可以做到模塊化開發、降低項目複雜性、提升部署效率和不同團隊之間項目耦合性的問題。

應用聚合,能夠解決用戶體驗,產品一致性等產品層面的問題,以及優化業務流程和定製化交付。

漸進式升級,相比較於全量升級的優點是,降低風險成本,適應性和靈活性也高,可以持續的靈活的把控重構進度,不影響現有業務。

你並不一定需要微前端

在決定使用微前端之前,首先要明白的是,微前端不是銀彈,微前端是和業務耦合性比較高的技術方案。微前端在解決實際場景下問題的同時,也帶來了更高的複雜性。

通常需要微前端方案解決的項目一般是 “巨石應用” 類,這類項目經過長期迭代維護,加之近些年前端技術的快速更新迭代,其的特點就是 業務邏輯複雜、龐大且技術棧老舊、兼容性差等。

在遇到問題,考慮使用微前端方案解決的同時,也要思考項目業務邏輯和技術複雜性的問題,考慮投入產出比。

爲什麼使用微前端

前端項目由各個部門前端團隊各自開發維護,沒有任何限制,主要有以下特點:

產品體驗

技術維度

由於歷史背景和包袱,加上中後臺系統的生命週期較長、技術棧老舊繁雜,導致很多問題,且隨着產品的持續迭代,問題會越發嚴重。其主要特性和問題包括但不限於以下點:

從業務本身的特性、現階段各產品項目形態、未來發展方向出發考慮,結合技術層面分析。現在遇到的問題,在未來會持續演化加重,造成的不利影響是全方面的。結合幾點來說,使用微前端可以比較好的解決上述痛點。

整體流程

這裏貼一張圖,大家可以參考:

微前端方案在業界比較成熟了,不同業務線,項目,場景可以結合使用不同的微前端框架和方案。過程都相差不大,分爲幾步:

通用業務邏輯集成

集成統一登錄、業務組織體系、消息工單、權限等通用邏輯。

路由導航控制

基座維護一份針對各子應用的路由表,可依據頁面功能劃分,對菜單項進行控制以及實現頁面上的產品拆分聚合。具體路由劃分由原系統域名和業務模塊進行配置對應,如:

基座除了維護子應用級的路由,還需要管理子應用下的一級路由,確保子應用內各功能的入口。

前期階段基座只維護子應用及子應用內的一級路由,後期將各子應用的路由、權限平臺化,由基座應用根據權限控制子應用及其功能。通過配置化、請求攔截,做到頁面級、按鈕級路由權限控制統一。

主題樣式控制

將適配業務主題的樣式變量整理統一,抽離出樣式配置表,通過基座下發給子應用。

通過基座維護的統一配置,可以做到全系統主題快速切換。

組件類庫共享

各項目在工程體系、技術棧、版本、分包策略等方面各不相同。如果對現有項目進行整合複用資源,面臨差異過大、調試成本等困難。

基座落地階段需要考慮到後續技術棧統一,主子應用之間資源共享的問題。基於後續規劃,基座考慮實現如下幾點確保資源最大化共享:

應用集成通信

現有各應用之間,除資源組、用戶等必要信息外,各應用、功能之間沒有過強的狀態依賴。得益於業務的這些特性,以及現有的項目實現流程,使得應用之間的通信需求比較簡單。另外,從應用設計層面來說,各應用之前不應過多耦合其它應用的業務邏輯,部分狀態的共享也可從後端獲取。各框架都提供了較爲完善的通信方案。

應用隔離

確保 js 和 css 的隔離方案,能夠兼容主子應用以及新老應用。

主子應用優化

除通過預加載的形式,可結合緩存、按需加載、依賴提取等方式提升子應用加載速度。對於一些高頻子應用,還可通過手動掛載應用,樣式隱藏的方式提升用戶體驗。

無侵入集成開發

子應用依賴基座的開發,通常需要啓動基座和子應用兩個服務,大多數情況下只需要開發對應子應用的功能。

開發方式有如下幾種:

建議第三種方案,提供一個腳手架工具,簡化工程師開發流程。同時集成項目所需模板、技術、規範、mock 等能力。

穩定性保障

技術和其他細節思考

微前端在解決問題的同時也會帶來一些不可知的問題和更高的複雜性。選擇合適的微前端方案,能減少踩坑的次數。

現在業界內微前端方案還是比較多的,結合實際情況選擇合適的微前端方案,可以幫助我們在實踐落地的過程中事半功倍。

原生技術

如果原生技術能滿足,越簡單越好

原生技術方案實現如:iframe、nginx 代理等,成本較低、接入簡單,但都具有片面性,比如:

  1. URL 不同步

  2. UI 不同步

  3. 通信麻煩

  4. 進入子應用需要重新加載資源,構建上下文

框架

微前端框架基本上要滿足,技術棧無關、應用加載、路由同步、通信、隔離、預加載等能力。

下面基於三個業界比較成熟完善、具有代表性的框架 qiankun,wujie、micro-app 簡單分析一下,在選擇微前端方案時需要關注的技術點。

css 隔離 - 單實例

原理:每次子應用加載時,刪除上一個子應的 link、style 樣式,只保留當前子應用的樣式,能有效區分子應用和子應用之前的樣式衝突。

css 隔離 - Scoped CSS

原理:改寫子應用所添加的樣式爲所有樣式規則增加一個特殊的選擇器規則來限定其影響範圍,達到樣式隔離的目的,類似 vue 的 scope-css。

.app-main {
  font-size: 14px;
}

div[data-qiankun-react16] .app-main {
  font-size: 14px;
}

由於需要在運行時替換子應用中所有的樣式規則,性能會受一定影響

css 隔離 - Shadow DOM

原理:爲每個微應用的容器包裹上一個 shadow dom 節點,從而確保微應用的樣式不會對全局造成影響。

css 隔離 - 規範性限制

依賴於規範,隔離效果不好。可以和微前端框架隔離方案,結合使用。比如:

單實例模式下,給主應用增加前綴命名空間,用來隔離主子應用的樣式。

js 隔離 - iframe

iframe 完美支持 js 隔離。比如,wujie 利用 iframe 的特性隔離 js,且解決了 iframe 的其他問題。

js 隔離 - 快照沙箱

在子應用加載和卸載時,對全局對象(如 window 對象)進行快照保存和恢復,從而確保各子應用之間的全局狀態互不干擾

保存全局狀態:在子應用加載之前,快照沙箱會對當前全局對象的狀態進行保存。遍歷全局對象(如 window)上的所有屬性,並將它們的值存儲在一個快照對象中。對每個屬性的值進行深拷貝,確保保存的是屬性的當前值,而不是引用。

恢復全局狀態:在子應用卸載之後,快照沙箱會恢復之前保存的全局狀態。

清空當前全局對象上的所有屬性,避免舊的子應用狀態影響新的子應用。將快照對象中的屬性值恢復到全局對象上,確保全局狀態回到子應用加載前的狀態。

js 隔離 - 代理沙箱

把當前 window 的一些原生屬性(如 document, location 等)拷貝出來,單獨放在一個對象上,這個對象也稱爲 fakeWindow,對每個微應用分配一個 fakeWindow,實現隔離。

當微應用修改全局變量時:如果是原生屬性,則修改全局的 window。如果不是原生屬性,則修改 fakeWindow 裏的內容。

微應用獲取全局變量時:如果是原生屬性,則從 window 裏獲取。如果不是原生屬性,則優先從 fakeWindow 裏獲取。

應用通信

如果是簡單傳值,使用 props 方式就行。如果主子應用之間需要頻繁交互,各微前端框架都有比較完善的通信機制。基本上都能滿足,結合實際項目選擇合適的即可。

多實例

支持多個子應用同時加載。

在實際的選擇過程中,需要考慮是否支持多實例模式。

預加載 & 應用保活

預加載和應用保活可以有效提升用戶體驗和頁面性能,如果對這方面有要求,在選擇的時候可以着重考慮。

預加載:空閒時加載資源,可以極大的提升子應用打開的首屏時間

應用保活:子應用只會進行一次渲染,數據和路由的狀態不會隨着頁面切換而丟失

子應用接入成本

根據接入子應用的數量、技術棧等方面考慮子應用接入成本。如果子應用的數量很多,需要根據每個子應用在技術側和業務側需要做的改動量進行權衡。

技術:

業務:

框架穩定性 & 社區活躍度

雖然現階段微前端方案較爲成熟,但是子應用在框架、UI 庫、編譯、版本存在的差異更大,無法 100% 確定在接入之後會遇到什麼問題。所以在技術選型的時候,要重視框架的穩定性和社區活躍度。

兼容性 & 性能

微前端框架的底層實現技術差異。

兼容性:

性能:

兼容性:web componts 和 proxy 實現的隔離方案 瀏覽器支持不太好。技術棧之間的兼容性,qiankun 對 vite 的支持不太好,因沙箱是 eval 執行 性能有點問題。還有一些版本上的差異 也會存在一定的兼容性問題 (angular antd 和 zone.js)

性能方面:各個框架性能差異不大,基本能滿足需求。(需要關注的是內存泄漏問題,在實際開發中,要確保子應用切換後被卸載)。

部署

考慮已有項目的部署方式結合前端路由,靜態資源、api 等方面來決定是否對子應用的部署方式進行改造。

如果是應用聚合子應用較多,各子應用都有自己的部署方案,建議使用主應用,對子應用的路由、資源和 api 進行代理,儘量減少減少子應用改動,同時也要考慮子應用是否需要獨立運行。

如果是項目重構或者拆分,子應用不多的情況下,可以在原有的部署方式進行調整,放在同一 ip 不同目錄下部署。

主子應用職責劃分思考

簡單的說,就是什麼功能放在主應用,什麼放在子應用。

主應用:微前端肯定是需要一個主應用的,那麼是新起一個項目作爲主應用,還是在原有項目上改造主應用?

技術側:如果本地環境下子應用資源加載出現了問題,是在主應用增加 devserver 的代理,還是更改子應用的 publicpath?

業務側:建議所有的統一邏輯,登錄、權限,layout 佈局等集成在主應用內。子應用只關注業務邏輯。但在實際的業務場景下,可能因爲業務邏輯的複雜性,跨部門溝通、子應用數量問題,後端實現邏輯複雜等原因造成業務改造的成本比較大。這時候在不影響體驗的情況下,部分邏輯也是可以放在子應用內獨自實現。

快速試錯

在選擇技術方案的時候,肯定是沒有完美的方案。遇到問題的時候,首先是要把問題進行分類,首先是框架和接入項目之間的問題,比如 wujie 影響富文本編輯器,single-spa 與 angular 的衝突等,如果遇到類似的問題,可以進行快速的試錯,實踐,嘗試其他的方案。

另外就是實現層面的問題,這種問題是可以確定能解決的,優先級可以放低。比如,基於 shodw dom 的隔離方案會影響 modal 的掛載樣式,這時候只需要換一種隔離方案或者統一調整 modal 的掛載節點。

應用顆粒度

合理的子應用顆粒度劃分,可以更好的管理項目。

應用聚合:其顆粒度本身就已經確定了就是每個接入的項目。具體每個項目還用不用拆分,可以具體分析。

應用拆分:顆粒度通常建議是按業務功能、團隊維護範圍進行拆分。

項目重構:最低只需要兩個應用就行了,一個是新的技術棧,一個是老的技術棧。將功能從老的技術棧逐步遷移到新的技術棧即可。當然也可以劃分更細的顆粒度。

其他

總結

上述便是在結合業務場景使用微前端方案的一些思考。希望能對大家有所幫助。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/vOHazTv6EMt9tgTb8P5-yg