目錄
微前端概念
微前端特性
場景演示
微前端方案?
iframe 方案
qiankun 方案
micro-app 方案
EMP 方案
無界微前端 方案
無界方案
成本低
速度快
原生隔離
功能強大
總結
前言:微前端已經是一個非常成熟的領域了,但開發者不管采用哪個現有方案,在適配成本、樣式隔離、運行性能、頁面白屏、子應用通信、子應用保活、多應用激活、vite 框架支持、應用共享等用戶核心訴求都或存在問題,或無法提供支持。本文提供一種基于 iframe 的全新微前端方案,完善的解決了這些核心訴求。?
微前端概念
微前端是借鑒了微服務的理念,將一個龐大的應用拆分成多個獨立靈活的小型應用,每個應用都可以獨立開發,獨立運行,獨立部署,還可以隨意組合,這樣就降低了耦合度,從而更加靈活。
微前端是一種架構風格,旨在將前端應用程序拆分為更小、更可管理的部分,以便能夠獨立開發、部署和維護。它的核心思想是將一個大型的前端應用程序拆分為多個小型的、獨立的子應用,每個子應用都可以獨立開發、測試、部署和運行。
在后臺管理系統中,微前端可以提供以下實際應用:
-
模塊化開發:通過微前端,可以將后臺管理系統拆分為多個獨立的模塊,每個模塊負責不同的功能或頁面。這樣可以實現團隊的并行開發,提高開發效率。
-
獨立部署:每個微前端子應用都可以獨立部署,這意味著當某個子應用需要更新或修復時,只需部署該子應用,而不會影響其他子應用的正常運行。這樣可以降低系統的維護成本,并提高系統的可用性。
-
技術棧靈活性:微前端允許每個子應用使用不同的技術棧,這樣可以根據不同的需求選擇最適合的技術棧。例如,可以使用React開發一個子應用,使用Vue開發另一個子應用,以及使用Angular開發第三個子應用。
-
增量升級:由于每個子應用都是獨立的,可以實現增量升級。當需要更新整個后臺管理系統時,只需更新相應的子應用,而不需要重新部署整個系統。
總之,微前端在后臺管理系統中的實際應用可以提供更好的團隊協作、模塊化開發、獨立部署和技術棧靈活性,從而提高開發效率和系統的可維護性
微前端特性
- 技術棧無關?主框架不限制接入應用的技術棧,子應用可自主選擇技術棧(vue,react,jq,ng等)
- 獨立開發/部署?各個團隊之間倉庫獨立,單獨部署,互不依賴
- 增量升級?當一個應用龐大之后,技術升級或重構相當麻煩,而微應用具備漸進式升級的特性
- 獨立運行時?微應用之間運行時互不依賴,有獨立的狀態管理
場景演示
- 后臺管理系統
最外面一層可以當主應用,里面可以放不同的子應用子應用不受技術的限制。
- web商店(未來趨勢)
例如一些導航網站,可以提供微前端的接入,我們的網站也可以入駐該網站,并且還可以提供一些API增加交互,有點類似于小程序。小程序可以調用微信的一些能力例如支付,掃碼等,導航類型的網站也可以提供一些API,我們的網站接入之后提供API調用,可以實現更多有趣的玩法。
微前端方案?
iframe 方案
特點
- 接入比較簡單
- 隔離非常穩完美
不足
- dom割裂感嚴重,彈框只能在iframe,而且有滾動條
- 通訊非常麻煩,而且刷新iframe url狀態丟失
- 前進后退按鈕無效
qiankun 方案
qiankun 方案是基于 single-spa 的微前端方案。
single-spa 是一種用于構建微前端架構的解決方案
single-spa 的核心思想是將前端應用程序拆分為多個獨立的子應用,每個子應用可以由不同的團隊開發和維護。這些子應用可以使用不同的框架或技術棧來構建,例如 React、Angular、Vue 等。single-spa 提供了一套機制來協調這些子應用的加載、啟動和通信,使它們能夠在同一個頁面上共存并相互配合工作。
特點
注解?entry
HTML entry 是一種將子應用嵌入到宿主應用的 HTML 頁面中的方法。通常,子應用會打包成一個獨立的 JavaScript 文件,然后在宿主應用的 HTML 頁面中通過?
<script>
?標簽引入這個 JavaScript 文件。這樣,當宿主應用加載時,子應用的 JavaScript 代碼也會被加載并執行,從而將子應用渲染到宿主應用的頁面中。除了 HTML entry,微前端還可以使用 JS entry 的方式引入子應用。JS entry 是一種將子應用打包成一個 JavaScript 模塊的方式,然后在宿主應用中通過動態加載的方式引入子應用的 JavaScript 模塊。
與 HTML entry 不同的是,JS entry 可以更加靈活地控制子應用的加載和卸載,因為子應用的 JavaScript 模塊可以通過動態加載的方式進行加載和卸載。此外,JS entry 還可以通過 Webpack 等打包工具實現代碼分割和按需加載,從而提高應用的性能和加載速度。
需要注意的是,使用 JS entry 的方式引入子應用需要考慮子應用的依賴關系和版本兼容性等問題,同時也需要考慮子應用的安全性和代碼質量等方面的問題。因此,在使用 JS entry 的方式引入子應用時,需要進行充分的測試和驗證,確保應用的穩定性和可靠性。
注解?沙箱(sandbox)?
沙箱(sandbox)是一種隔離機制,用于在微前端架構中保護子應用之間的相互影響,確保它們在運行時不會相互干擾。在微前端中,常見的沙箱機制包括 JavaScript 沙箱和 CSS 沙箱。
JavaScript 沙箱:
- SnapshotSandbox:SnapshotSandbox 是一種基于 JavaScript 快照技術的沙箱實現。它通過在子應用加載前對全局對象進行快照,并在子應用執行完畢后恢復快照,從而隔離了子應用的全局變量和函數,防止子應用之間的命名沖突和全局污染。
- LegacySandbox:LegacySandbox 是一種傳統的沙箱實現,它使用 iframe 元素來隔離子應用的 JavaScript 代碼。每個子應用都在獨立的 iframe 中運行,這樣可以確保子應用之間的 JavaScript 代碼互相隔離,避免全局污染和沖突。
ProxySandbox基于ES6中的Proxy對象,它允許我們攔截并修改JavaScript對象的操作。通過使用ProxySandbox,我們可以創建一個代理層,攔截對應用程序的訪問,并根據定義的規則來限制或修改這些訪問。
ProxySandbox的主要思想是在應用程序加載之前,創建一個代理對象,該代理對象會攔截對應用程序的訪問,并根據預先定義的規則來處理這些訪問。這樣,即使應用程序中存在惡意代碼或不受信任的代碼,也可以通過代理對象進行控制,以確保安全性。
CSS 沙箱:
- strictStyleIsolation:strictStyleIsolation 是一種嚴格的 CSS 沙箱實現。它通過將子應用的樣式表限制在宿主應用的特定區域內,避免子應用的樣式影響到其他子應用或宿主應用。這種沙箱方式可以確保樣式的隔離性和可控性,但可能會導致一些樣式的失效或沖突。
- experimentalStyleIsolation:experimentalStyleIsolation 是一種實驗性的 CSS 沙箱實現。它使用 Shadow DOM 技術將子應用的樣式隔離在 Shadow DOM 中,從而實現更加徹底的樣式隔離。這種沙箱方式可以避免樣式的沖突和失效,但在一些舊版瀏覽器中可能不被完全支持。
- html entry 的方式引入子應用,相比 js entry 極大的降低了應用改造的成本;
- 完備的沙箱方案,js 沙箱做了 SnapshotSandbox、LegacySandbox、ProxySandbox 三套漸進增強方案,css 沙箱做了 strictStyleIsolation、experimentalStyleIsolation 兩套適用不同場景的方案;
- 做了靜態資源預加載能力;
不足
- 適配成本比較高,工程化、生命周期、靜態資源路徑、路由等都要做一系列的適配工作;
- css 沙箱采用嚴格隔離會有各種問題,js 沙箱在某些場景下執行性能下降嚴重;
- 無法同時激活多個子應用,也不支持子應用保活;
- 無法支持 vite 等 esmodule 腳本運行;
底層原理 js沙箱使用的是proxy進行快照然后用用 with(window){} 包裹起來 with內的window其實就是proxy.window 我們聲明變量 var name = '小滿' 實際這個變量掛到了proxy.window 并不是真正的window css沙箱原理 第一個就是shadowDom隔離 第二個類似于Vue的scoped [data-qiankun-426732]
micro-app 方案
micro-app 是基于 webcomponent + qiankun sandbox 的微前端方案。
Web Components 是一種瀏覽器原生支持的技術,它允許開發者創建自定義的可重用組件。Web Components 使用了一系列的標準,包括自定義元素、Shadow DOM、HTML 模板和 HTML Imports。通過使用 Web Components,開發者可以將組件封裝為獨立的、可復用的模塊,然后在不同的項目中使用。
特點
- 使用 webcomponet 加載子應用相比 single-spa 這種注冊監聽方案更加優雅;
- 復用經過大量項目驗證過 qiankun 的沙箱機制也使得框架更加可靠;
- 組件式的 api 更加符合使用習慣,支持子應用保活;
- 降低子應用改造的成本,提供靜態資源預加載能力;
不足
微前端中的虛擬路由是指在微前端架構中,通過前端路由來實現不同微前端應用之間的無縫集成。虛擬路由的實現方式是在主應用中定義一組虛擬路由規則,然后將這些規則映射到各個微前端應用的實際路由上。
具體來說,虛擬路由可以通過以下步驟實現:
在主應用中定義一組虛擬路由規則,例如:/app1、/app2、/app3 等。
在微前端應用中定義實際的路由規則,例如:/app1/home、/app1/about、/app2/home、/app2/about 等。
在主應用中,將虛擬路由規則映射到各個微前端應用的實際路由上,例如:將 /app1 映射到微前端應用1的 /app1 路由上,將 /app2 映射到微前端應用2的 /app2 路由上。
當用戶訪問主應用中的虛擬路由時,主應用會根據映射關系將請求轉發到對應的微前端應用中,從而實現不同微前端應用之間的無縫集成。
虛擬路由的實現可以大大簡化微前端應用之間的集成,提高開發效率和用戶體驗。
- 接入成本較 qiankun 有所降低,但是路由依然存在依賴; (虛擬路由已解決)
- 多應用激活后無法保持各子應用的路由狀態,刷新后全部丟失; (虛擬路由已解決)
- css 沙箱依然無法絕對的隔離,js 沙箱做全局變量查找緩存,性能有所優化;
- 支持 vite 運行,但必須使用 plugin 改造子應用,且 js 代碼沒辦法做沙箱隔離;
- 對于不支持 webcompnent 的瀏覽器沒有做降級處理;
底層原理 js隔離跟qiankun類似也是使用proxy + with,css隔離自定義前綴類似于scoped
EMP 方案
EMP 方案是基于 webpack 5 module federation 的微前端方案。
特點
- webpack 聯邦編譯可以保證所有子應用依賴解耦;
- 應用間去中心化的調用、共享模塊;
- 模塊遠程 ts 支持;
不足
- 對 webpack 強依賴,老舊項目不友好;
- 沒有有效的 css 沙箱和 js 沙箱,需要靠用戶自覺;
- 子應用保活、多應用激活無法實現;
- 主、子應用的路由可能發生沖突;
底層原理 這個東西有點類似于拆包,也可以叫模塊共享,例如React有個模塊可以共享給Vue項目用Vue2的組件可以共享給Vue3用。
無界微前端 方案
預覽demo wujie-micro.github.io/demo-main-v…https://link.juejin.cn?target=https%3A%2F%2Fwujie-micro.github.io%2Fdemo-main-vue%2F**home
特點
- 接入簡單只需要四五行代碼
- 不需要針對vite額外處理
- 預加載
- 應用保活機制
不足
- 隔離js使用一個空的iframe進行隔離
- 子應用axios需要自行適配
- iframe沙箱的src設置了主應用的host,初始化iframe的時候需要等待iframe的location.orign從'about:blank'初始化為主應用的host,這個采用的計時器去等待的不是很悠亞。
底層原理 使用shadowDom 隔離css,js使用空的iframe隔離,通訊使用的是proxy
無界方案
無界微前端方案基于 webcomponent 容器 + iframe 沙箱,能夠完善的解決適配成本、樣式隔離、運行性能、頁面白屏、子應用通信、子應用保活、多應用激活、vite 框架支持、應用共享等用戶的核心訴求。
文檔地址,demo 地址,git 地址
下面就成本、速度、隔離、功能等多個方面進行闡述。
成本低
無界微前端的成本非常低,主要體現在主應用的使用成本、子應用的適配成本兩個方面。
主應用使用成本
主應用使用無界不需要學習額外的知識,無界提供基于 vue 封裝的 wujie-vue 和基于 react 封裝的 wujie-react,用戶可以當初普通組件一樣加載子應用,以 wujie-vue 舉例:
子應用適配成本
子應用首先需要做支持跨域請求改造,這個是所有微前端框架運行的前提,除此之外子應用可以不做任何改造就可以在無界框架中運行,不過此時運行的方式是重建模式。
子應用在無界中會根據是否保活、是否做了生命周期適配進入不同的運行模式:
其中保活模式、單例模式、重建模式適用于不同的業務場景,就算復雜點的單例模式用戶也只是需要做一點簡單的生命周期改造工作,可以說子應用適配成本極低。
保活模式?
子應用的?alive?設置為
true
時進入保活模式,內部的數據和路由的狀態不會隨著頁面切換而丟失。在保活模式下,子應用只會進行一次渲染,頁面發生切換時承載子應用
dom
的webcomponent
會保留在內存中,當子應用重新激活時無界會將內存中的webcomponent
重新掛載到容器上保活模式下改變?url?子應用的路由不會發生變化,需要采用?通信?的方式對子應用路由進行跳轉
注意
保活的子應用的實例不會銷毀,子應用被切走了也可以響應 bus 事件,非保活的子應用切走了監聽的事件也會全部銷毀,需要等下次重新 mount 后重新監聽。
單例模式?
子應用的
alive
為false
且進行了生命周期改造時進入單例模式。子應用頁面如果切走,會調用
window.__WUJIE_UNMOUNT
銷毀子應用當前實例,子應用頁面如果切換回來,會調用window.__WUJIE_MOUNT
渲染子應用新的子應用實例在單例式下,改變?url?子應用的路由會發生跳轉到對應路由
如果主應用上有多個菜單欄用到了子應用的不同頁面,在每個頁面啟動該子應用的時候將
name
設置為同一個,這樣可以共享一個wujie
實例,承載子應用js
的iframe
也實現了共享,不同頁面子應用的url
不同,切換這個子應用的過程相當于:銷毀當前應用實例 => 同步新路由 => 創建新應用實例重建模式?
子應用既沒有設置為保活模式,也沒有進行生命周期的改造則進入了重建模式,每次頁面切換不僅會銷毀承載子應用
dom
的webcomponent
,還會銷毀承載子應用js
的iframe
,相應的wujie
實例和子應用實例都會被銷毀重建模式下改變?url?子應用的路由會跳轉對應路由,但是在?路由同步?場景并且子應用的路由同步參數已經同步到主應用
url
上時則無法生效,因為改變url
后會導致子應用銷毀重新渲染,此時如果有同步參數則同步參數的優先級最高
速度快
無界微前端非常快,主要體現在首屏打開快、運行速度快兩個方面。
首屏打開快
目前大部分微前端只能做到靜態資源預加載,但是就算子應用所有資源都預加載完畢,等到子應用打開時頁面仍然有不短的白屏時間,這部分白屏時間主要是子應用 js 的解析和執行。
無界微前端不僅能夠做到靜態資源的預加載,還可以做到子應用的預執行。
預執行會阻塞主應用的執行線程,所以無界提供 fiber 執行模式,采取類似 react fiber 的方式間斷執行 js,每個 js 文件的執行都包裹在 requestidlecallback 中,每執行一個 js 可以返回響應外部的輸入,但是這個顆粒度是 js 文件,如果子應用單個 js 文件過大,可以通過拆包的方式降低體積達到 fiber 執行模式效益最大化。
運行速度快
子應用的 js 在 iframe 內運行,由于 iframe 是一個天然的 js 運行沙箱,所以無需采用 with ( fakewindow ) 這種方式來指定子應用的執行上下文,從而避免由于采用 with 語句執行子應用代碼而導致的性能下降,整體的運行性能和原生性能差別不大。
原生隔離
無界微前端實現了 css 沙箱和 js 沙箱的原生隔離,子應用不用擔心污染問題。
css 沙箱隔離
無界將子應用的 dom 放置在 webcomponent + shadowdom 的容器中,除了可繼承的 css 屬性外實現了應用之間 css 的原生隔離。
js 沙箱隔離
無界將子應用的 js 放置在 iframe(js-iframe)中運行,實現了應用之間 window、document、location、history 的完全解耦和隔離。
js 沙箱和 css 沙箱連接
無界在底層采用 proxy + Object.defineproperty 的方式將 js-iframe 中對 dom 操作劫持代理到 webcomponent shadowRoot 容器中,開發者無感知也無需關心。
功能強大
無界微前端的功能非常強大,支持子應用保活、子應用內嵌、多應用激活、去中心化通信、生命周期、插件系統、vite 框架支持、兼容 IE9、應用共享。
子應用保活
當子應用設置為保活模式,切換子應用后仍然可以保持子應用的狀態和路由不會丟失。
子應用嵌套
無界支持子應用多層嵌套,嵌套的應用和正常應用一致,支持預加載、保活、同步、通信等能力,需要注意的是內嵌的子應用 name 也需要保持唯一性,否則將復用之前渲染出來的應用
多應用激活
無界支持一個頁面同時激活多個子應用并且保持這些子應用路由同步的能力。
去中心化通信
無界提供多種通信方式:window.parent 直接通信、props 數據注入、去中心化 EventBus 通信機制:
- 子應用 js 在和主應用同域的 iframe 內運行,所以 window.parent 可以直接拿到主應用的 window 對象來進行通信
- 主應用可以向子應用注入 props 對象,里面可以注入數據和方法供子應用調用
- 內置的 EventBus 去中心化通信方案可以讓應用之間方便的直接通信
生命周期
無界提供完善的生命周期鉤子供主應用調用:
- beforeLoad:子應用開始加載靜態資源前觸發
- beforeMount:子應用渲染前觸發 (生命周期改造專用)
- afterMount:子應用渲染后觸發(生命周期改造專用)
- beforeUnmount:子應用卸載前觸發(生命周期改造專用)
- afterUnmount:子應用卸載后觸發(生命周期改造專用)
- activated:子應用進入后觸發(保活模式專用)
- deactivated:子應用離開后觸發(保活模式專用)
插件系統
無界提供強大的插件系統,方便用戶在運行時去修改子應用代碼從而避免將適配代碼硬編碼到倉庫中。
無界插件主要能力如下:
- html-loader 可以對子應用 template 進行處理
- js-excludes 和 css-excludes 可以排除子應用特定的 js 和 css 加載
- js-before-loaders、js-loader、js-after-loaders 可以方便的對子應用 js 進行自定義
- css-before-loaders、css-loader、css-after-loaders 可以方便的對子應用 css 進行自定義
vite 框架支持
無界子應用運行在 iframe 中原生支持 esm 的腳本,而且不用擔心子應用運行的上下文問題,因為子應用讀取的就是 iframe 的 window 上下文,所以無界微前端原生支持 vite 框架。
應用共享
一個微前端系統可能同時運行多個子應用,不同子應用之間可能存在相同的包依賴,那么這個依賴就會在不同子應用中重復打包、重復執行造成性能和內存的浪費。
無界提供一種工程上的策略結合無界的插件能力,可以有效的解決這個問題(其他微前端框架也可以做到),這里以一個場景舉例:主應用使用到了 ant-design-vue,子應用 A 也使用到了相同版本的 ant-design-vue。
主應用:
1、修改主應用的 index.js,將共享包掛載到主應用的 window 對象上
// index.js
import Antdv from "ant-design-vue";
// 將需要共享的包掛載到主應用全局
window.Antdv = Antdv;
2、加載子應用時注入插件,將主應用的 Antdv 賦值到子應用的 window 對象上
<WujieVue name="A" url="xxxxx" :plugins="[{ jsBeforeLoaders: [{ content: 'window.Antdv = window.parent.Antdv' }] }]">
</WujieVue>
子應用: webpack 設置 externals
module.exports = {externals: {"ant-design-vue": {root: "Antdv",commonjs: "Antdv",commonjs2: "Antdv",amd: "Antdv",},},
};
總結
無界微前端采用 webcomponent + iframe 的來加載子應用,具有成本低、速度快、原生隔離、功能強大等一系列優點,在滿足用戶核心訴求的同時讓使用微前端的體驗就像使用普通組件一樣簡單,極大的降低了使用門檻。