轉(zhuǎn)載請(qǐng)注明出處,點(diǎn)擊此處 查看更多精彩內(nèi)容瘾境。
iframe
iframe 是 HTML 的內(nèi)聯(lián)框架元素喳篇,表示嵌套的 Browsing Context迅矛,它能夠?qū)⒘硪粋€(gè) HTML 頁(yè)面嵌入到當(dāng)前頁(yè)面中型宝,每個(gè)嵌入的 Browsing Context
都有自己的會(huì)話歷史記錄和 DOM 樹械筛。
每個(gè)瀏覽上下文都擁有完整的文檔環(huán)境捎泻,因此頁(yè)面上每個(gè) iframe
都需要增加內(nèi)存和其它計(jì)算資源,雖然理論上來(lái)說能夠在代碼中寫出來(lái)無(wú)限多的 iframe
埋哟,但是這么做可能會(huì)導(dǎo)致某些性能問題笆豁。
優(yōu)點(diǎn)
- Web 應(yīng)用隔離的非常完美,無(wú)論是 JavaScript赤赊、CSS闯狱、DOM 都完全隔離開來(lái)。
- 非常簡(jiǎn)單抛计,使用沒有任何心智負(fù)擔(dān)哄孤。
缺點(diǎn)
- DOM 結(jié)構(gòu)不共享,彈窗只能在
iframe
內(nèi)部展示吹截,無(wú)法覆蓋全局瘦陈。 - 路由狀態(tài)丟失凝危,刷新頁(yè)面會(huì)導(dǎo)致
iframe
的 url 狀態(tài)丟失、后退前進(jìn)按鈕無(wú)法使用晨逝。 - 性能差蛾默,每次子應(yīng)用進(jìn)入都是一次瀏覽器上下文重建、資源重新加載的過程捉貌。
- 應(yīng)用之間通信困難支鸡,全局上下文完全隔離,內(nèi)存變量不共享趁窃,無(wú)法訪問非同源的
window
對(duì)象的幾乎所有屬性牧挣,跨域通信僅可通過window.postMessage()
來(lái)實(shí)現(xiàn)。
路由轉(zhuǎn)發(fā)
路由分發(fā)式微前端醒陆,即通過路由將不同的業(yè)務(wù)分發(fā)到不同的獨(dú)立前端應(yīng)用上瀑构。最常用的方案是通過 HTTP 服務(wù)的反向代理(如 Nginx)將不同頁(yè)面的請(qǐng)求分發(fā)到不同的服務(wù)上。
優(yōu)點(diǎn)
- 實(shí)現(xiàn)簡(jiǎn)單刨摩。
- 維護(hù)检碗、開發(fā)成本低。
- 不需要對(duì)現(xiàn)有應(yīng)用進(jìn)行改造码邻。
缺點(diǎn)
- 用戶體驗(yàn)不好折剃,每次切換應(yīng)用時(shí),瀏覽器都需要重新加載頁(yè)面像屋。
- 子應(yīng)用之間的通信比較困難怕犁。
- 多個(gè)子應(yīng)用無(wú)法并存。
- 子應(yīng)用切換時(shí)需要重新登錄己莺。
動(dòng)態(tài)加載模塊
創(chuàng)建一個(gè)基座應(yīng)用奏甫,允許子應(yīng)用單獨(dú)部署。為了實(shí)現(xiàn)這一點(diǎn)凌受,創(chuàng)建一個(gè) manifest
文件阵子,記錄上線的子應(yīng)用及版本信息,當(dāng)子應(yīng)用部署更新時(shí)修改 manifest
文件胜蛉,基座應(yīng)用通過 manifest
檢查更新子應(yīng)用資源挠进。
改變每個(gè)子應(yīng)用加載的 JavaScript 文件有很多的方法:
- Web 服務(wù)器:在你的 Web 服務(wù)器為每個(gè)子應(yīng)用的正確版本創(chuàng)建一個(gè)動(dòng)態(tài)腳本。
- 使用模塊加載 例如 SystemJS 可以在瀏覽器通過動(dòng)態(tài) urls 下載并執(zhí)行 JavaScript 代碼誊册。
npm
每個(gè)子應(yīng)用在一個(gè)單獨(dú)的代碼倉(cāng)庫(kù)中领突,每次更新時(shí)發(fā)布一個(gè)新版本到 npm,創(chuàng)建一個(gè)基座應(yīng)用案怯,通過 npm 安裝每個(gè)子應(yīng)用君旦。
優(yōu)點(diǎn)
- npm 安裝對(duì)于開發(fā)中更熟悉,易于搭建。
缺點(diǎn)
- 子應(yīng)用發(fā)生變更時(shí)金砍,基座應(yīng)用需要重新安裝局蚀、重新構(gòu)建和重新部署。
- 無(wú)法動(dòng)態(tài)安裝恕稠、卸載子應(yīng)用至会。
Web Component
Web Component 是一套不同的技術(shù),允許你創(chuàng)建可重用的定制元素(它們的功能封裝在你的代碼之外)并且在你的 Web 應(yīng)用中使用它們谱俭。
Web Components
由三項(xiàng)主要技術(shù)組成,它們可以一起使用來(lái)創(chuàng)建封裝功能的定制元素宵蛀,可以在你喜歡的任何地方重用昆著,不必?fù)?dān)心代碼沖突。
- Custom Element(自定義元素)
一組 JavaScript API术陶,允許您定義 Custom Elements 及其行為凑懂,然后可以在您的用戶界面中按照需要使用它們。 - Shadow DOM(影子 DOM)
一組 JavaScript API梧宫,用于將封裝的“影子”DOM 樹附加到元素(與主文檔 DOM 分開呈現(xiàn))并控制其關(guān)聯(lián)的功能接谨。通過這種方式,您可以保持元素的功能私有塘匣,這樣它們就可以被腳本化和樣式化脓豪,而不用擔(dān)心與文檔的其他部分發(fā)生沖突。 - HTML Template(HTML 模板)
<template>
和<slot>
元素使您可以編寫不在呈現(xiàn)頁(yè)面中顯示的標(biāo)記模板忌卤。然后它們可以作為自定義元素結(jié)構(gòu)的基礎(chǔ)被多次重用扫夜。
基于 Web Component
的 Shadow Dom
能力,我們也可以實(shí)現(xiàn)微前端驰徊,將多個(gè)子應(yīng)用聚合起來(lái)笤闯。
const shadow = document.querySelector('#hostElement').attachShadow({mode: 'open'});
// url 為應(yīng)用的地址,基于 fetch棍厂,我們可以獲取到應(yīng)用的 html 模板颗味,添加到指定節(jié)點(diǎn)下
fetch(url).then(res => {
shadow.innerHTML = res
});
優(yōu)點(diǎn)
- 實(shí)現(xiàn)簡(jiǎn)單。
- 完全技術(shù)棧無(wú)關(guān)牺弹。
- 不需要對(duì)現(xiàn)有應(yīng)用進(jìn)行改造浦马。
- CSS 和 JavaScript 天然隔離,互不干擾张漂。
缺點(diǎn)
- 沒有統(tǒng)一的
Web Component
規(guī)范捐韩。 - 瀏覽器實(shí)現(xiàn)不一致。
- Web 組件存在向后不兼容的版本問題鹃锈。
- 開發(fā)成本較高荤胁。
EMP
EMP 基于 Webpack5 的 Module Federation 實(shí)現(xiàn),用一個(gè)詞概括屎债,就是“去中心化”仅政,在 EMP 的方案中不需要中心化的基座垢油,每一個(gè)微前端應(yīng)用都可以通過遠(yuǎn)程調(diào)用的方式引入共享模塊。
優(yōu)點(diǎn)
- Webpack5 Module Federation 可以保證所有子應(yīng)用依賴解耦圆丹。
- 模塊遠(yuǎn)程 TypeScript 支持滩愁。
- 應(yīng)用間去中心化的調(diào)用、共享模塊辫封。
缺點(diǎn)
- 對(duì) Webpack 強(qiáng)依賴硝枉,老舊項(xiàng)目不友好。
- 沒有有效的 CSS 沙箱和 JavaScript 沙箱倦微,需要靠用戶自覺妻味。
- 主、子應(yīng)用的路由可能發(fā)生沖突欣福。
- 子應(yīng)用痹鹎颍活、多應(yīng)用激活無(wú)法實(shí)現(xiàn)拓劝。
single-spa / qiankun
single-spa 是一個(gè)目前主流的微前端技術(shù)方案雏逾。
single-spa
從現(xiàn)代框架組件生命周期中獲得靈感,將生命周期應(yīng)用于整個(gè)應(yīng)用程序郑临,其主要實(shí)現(xiàn)思路:
- 預(yù)先注冊(cè)子應(yīng)用(激活路由栖博、子應(yīng)用資源、生命周期函數(shù))厢洞。
- 監(jiān)聽路由的變化笛匙,匹配到了激活的路由則加載子應(yīng)用資源,順序調(diào)用生命周期函數(shù)并最終渲染到容器犀变。
single-spa
未解決子應(yīng)用加載妹孙、應(yīng)用隔離、子應(yīng)用通信等問題获枝。
qiankun 微前端架構(gòu)則進(jìn)一步對(duì) single-spa
方案進(jìn)行完善蠢正,主要的完善點(diǎn):
- 子應(yīng)用資源由 JavaScript 列表修改進(jìn)為一個(gè) url,大大減輕注冊(cè)子應(yīng)用的復(fù)雜度省店。
- 增加資源預(yù)加載能力嚣崭,預(yù)先緩存子應(yīng)用的 HTML、JavaScript懦傍、CSS 資源雹舀,加快子應(yīng)用的打開速度。
- 實(shí)現(xiàn)應(yīng)用隔離粗俱,完成 JavaScript 隔離方案 (window 工廠) 和 CSS 隔離方案 (類 Vue 的 scoped)说榆。
- 提供了 @umijs/plugin-qiankun 供 umi 應(yīng)用一鍵切換成微前端架構(gòu)系統(tǒng)。
優(yōu)點(diǎn)
- 監(jiān)聽路由自動(dòng)的加載、卸載當(dāng)前路由對(duì)應(yīng)的子應(yīng)用签财。
- 路由保持串慰,瀏覽器刷新、前進(jìn)唱蒸、后退邦鲫,都可以作用到子應(yīng)用。
- 應(yīng)用間通信簡(jiǎn)單神汹,全局注入庆捺。
- 完備的沙箱方案,JavaScript 沙箱做了
SnapshotSandbox
屁魏、LegacySandbox
滔以、ProxySandbox
三套漸進(jìn)增強(qiáng)方案,CSS 沙箱做了兩套strictStyleIsolation
蚁堤、experimentalStyleIsolation
兩套適用不同場(chǎng)景的方案。
缺點(diǎn)
- 改造成本較大但狭,從 Webpack披诗、代碼、路由等等都要做一系列的適配立磁。
- 基于路由匹配呈队,無(wú)法同時(shí)激活多個(gè)子應(yīng)用,也不支持子應(yīng)用背纾活宪摧。
- CSS 沙箱無(wú)法絕對(duì)的隔離,JavaScript 沙箱在某些場(chǎng)景下執(zhí)行性能下降嚴(yán)重颅崩。
- 無(wú)法支持
Vite
等 ESM 腳本運(yùn)行几于。
無(wú)界(wujie)
無(wú)界利用 iframe
的沙箱機(jī)制,將子應(yīng)用的 JavaScript 注入到基座應(yīng)用同域的 iframe
中運(yùn)行沿后,并采用 Web Component
實(shí)現(xiàn)頁(yè)面的樣式隔離沿彭。
- 應(yīng)用加載機(jī)制和 JavaScript 沙箱機(jī)制
將子應(yīng)用的 JavaScript 注入主應(yīng)用同域的iframe
中運(yùn)行,iframe
是一個(gè)原生的window
沙箱尖滚,內(nèi)部有完整的history
和location
接口喉刘,子應(yīng)用實(shí)例運(yùn)行在iframe
中,路由主應(yīng)用解耦漆弄,可以直接在業(yè)務(wù)組件里面啟動(dòng)應(yīng)用睦裳。 - 路由同步機(jī)制
劫持iframe
的history.pushState
和history.replaceState
,就可以將子應(yīng)用的 url 同步到主應(yīng)用的 query 參數(shù)上撼唾,當(dāng)刷新瀏覽器初始化iframe
時(shí)廉邑,讀回子應(yīng)用的 url 并使用iframe
的history.replaceState
進(jìn)行同步。 -
iframe
連接機(jī)制和 CSS 沙箱機(jī)制
無(wú)界采用Web Component
來(lái)實(shí)現(xiàn)頁(yè)面的樣式隔離,無(wú)界會(huì)創(chuàng)建一個(gè)wujie
自定義元素鬓催,然后將子應(yīng)用的完整結(jié)構(gòu)渲染在內(nèi)部肺素。子應(yīng)用的實(shí)例在iframe
內(nèi)運(yùn)行,DOM 在主應(yīng)用容器下的Web Component
內(nèi)宇驾,通過代理iframe
的document
到Web Component
倍靡,可以實(shí)現(xiàn)兩者的互聯(lián)。 - 通信機(jī)制
承載子應(yīng)用的iframe
和主應(yīng)用是同域的课舍,所以主塌西、子應(yīng)用天然就可以很好的進(jìn)行通信,無(wú)界提供三種通信方式筝尾。-
props
注入機(jī)制捡需,子應(yīng)用通過$wujie.props
可以輕松拿到主應(yīng)用注入的數(shù)據(jù)。 - 子應(yīng)用
iframe
沙箱和主應(yīng)用同源筹淫,子應(yīng)用可以直接通過window.parent
和主應(yīng)用通信站辉。 - 無(wú)界提供了
EventBus
實(shí)例,注入到主應(yīng)用和子應(yīng)用损姜,所有的應(yīng)用可以去中心化的進(jìn)行通信饰剥。
-
優(yōu)點(diǎn)
- 多應(yīng)用同時(shí)激活在線
框架具備同時(shí)激活多應(yīng)用,并保持這些應(yīng)用路由同步的能力摧阅。 - 組件式的使用方式
無(wú)需注冊(cè)汰蓉,更無(wú)需路由適配,在組件內(nèi)使用棒卷,跟隨組件裝載顾孽、卸載。 - 應(yīng)用級(jí)別的
keep-alive
子應(yīng)用開啟北裙妫活模式后若厚,應(yīng)用發(fā)生切換時(shí)整個(gè)子應(yīng)用的狀態(tài)可以保存下來(lái)不丟失,結(jié)合預(yù)執(zhí)行模式可以獲得類似 SSR 的打開體驗(yàn)蜒什。 - 純凈無(wú)污染
- 無(wú)界利用
iframe
和Web Component
來(lái)搭建天然的 JavaScript 隔離沙箱和 CSS 隔離沙箱盹沈。 - 利用
iframe
的history
和主應(yīng)用的history
在同一個(gè) top-levelBrowsing Context
來(lái)搭建天然的路由同步機(jī)制。 - 副作用局限在沙箱內(nèi)部吃谣,子應(yīng)用切換無(wú)需任何清理工作乞封,沒有額外的切換成本。
- 無(wú)界利用
- 性能和體積兼具
- 子應(yīng)用執(zhí)行性能和原生一致岗憋,子應(yīng)用實(shí)例運(yùn)行在
iframe
的window
上下文中肃晚,避免with(proxyWindow){code}
這樣指定代碼執(zhí)行上下文導(dǎo)致的性能下降,但是多了實(shí)例化iframe
的一次性的開銷仔戈,可以通過 preload 提前實(shí)例化关串。 - 體積比較輕量拧廊,借助
iframe
和Web Component
來(lái)實(shí)現(xiàn)沙箱,有效的減小了代碼量晋修。
- 子應(yīng)用執(zhí)行性能和原生一致岗憋,子應(yīng)用實(shí)例運(yùn)行在
- 開箱即用
不管是樣式的兼容吧碾、路由的處理、彈窗的處理墓卦、熱更新的加載倦春,子應(yīng)用完成接入即可開箱即用無(wú)需額外處理,應(yīng)用接入成本也極低落剪。 - 子應(yīng)用運(yùn)行在
iframe
中睁本,原生支持 ESM 的腳本。
缺點(diǎn)
-
iframe
的一些坑尚未解決忠怖,如路由回退問題呢堰。
micro-app
micro-app 借鑒了 Web Component
的思想,通過 Custom Element
結(jié)合自定義的 Shadow Dom
凡泣,將微前端封裝成一個(gè)類 Web Component
組件枉疼,從而實(shí)現(xiàn)微前端的組件化渲染。并且由于自定義 Shadow Dom
的隔離特性鞋拟,micro-app
不需要像 single-spa
和 qiankun
一樣要求子應(yīng)用修改渲染邏輯并暴露出方法骂维,也不需要修改 Webpack 配置,在基座應(yīng)用中嵌入一行代碼即可渲染一個(gè)微前端應(yīng)用严卖,是目前市面上接入微前端成本最低的方案席舍。
優(yōu)點(diǎn)
- 零依賴
micro-app
沒有任何依賴布轿,這賦予它小巧的體積和更高的擴(kuò)展性哮笆。 - 使用簡(jiǎn)單
我們將所有功能都封裝到一個(gè)類Web Component
組件中,從而實(shí)現(xiàn)在基座應(yīng)用中嵌入一行代碼即可渲染一個(gè)微前端應(yīng)用汰扭。同時(shí)micro-app
還提供了 JavaScript 沙箱稠肘、樣式隔離、元素隔離萝毛、預(yù)加載项阴、數(shù)據(jù)通信、靜態(tài)資源補(bǔ)全等一系列完善的功能笆包。 - 兼容所有框架
為了保證各個(gè)業(yè)務(wù)之間獨(dú)立開發(fā)环揽、獨(dú)立部署的能力,micro-app
做了諸多兼容庵佣,在任何技術(shù)框架中都可以正常運(yùn)行歉胶。
缺點(diǎn)
- 多應(yīng)用激活后無(wú)法保持子應(yīng)用的路由狀態(tài),刷新后全部丟失巴粪。
- 對(duì)于不支持
Web Component
的瀏覽器沒有做降級(jí)處理通今。 - 支持
Vite
運(yùn)行粥谬,但必須使用plugin
改造子應(yīng)用,且 JavaScript 代碼沒辦法做沙箱隔離辫塌。 - 較長(zhǎng)時(shí)間未更新漏策,穩(wěn)定版最近一次發(fā)版(v0.8.10)時(shí)間是 2022.08.19,測(cè)試版最近一次發(fā)版(v1.0.0-alpha.10)時(shí)間是 2022.10.11臼氨。