什么是微前端?運(yùn)行時(shí)微前端有哪些具體實(shí)現(xiàn)方式?
三、如何實(shí)現(xiàn)微前端架構(gòu)
微前端不是一個(gè)庫,是一種前端架構(gòu)的設(shè)計(jì)思路,要實(shí)現(xiàn)微前端,本質(zhì)上就是在運(yùn)行時(shí)遠(yuǎn)程加載應(yīng)用。
實(shí)現(xiàn)微前端,有幾個(gè)思路,從構(gòu)建的角度來看有兩種,編譯時(shí)構(gòu)建微前端和運(yùn)行時(shí)構(gòu)建微前端:
編譯時(shí)微前端,通常將第三方庫中的組件作為包,在構(gòu)建時(shí)引入依賴。這種實(shí)現(xiàn)引入新的微前端需要重新編譯,不夠靈活。編譯時(shí)的微前端可以通過Web Components,Monorepo等方式來實(shí)現(xiàn)。其中Monorepo非常流行,常見的工具有nx,rush,lerna等。
運(yùn)行時(shí)微前端,是一次加載或通過延遲加載按需動(dòng)態(tài)將微型前端注入到容器應(yīng)用程序中時(shí)。當(dāng)引入新的微前端的時(shí)候,不需要構(gòu)建,可以動(dòng)態(tài)在代碼中定義加載。我眼中的微前端更多是指這種運(yùn)行時(shí)加載的微前端,因?yàn)楠?dú)立構(gòu)建,部署和測(cè)試是我們對(duì)于“微”的定義。
從前后端責(zé)任分層來看,可以從前端或者后端來實(shí)現(xiàn)。
通過客戶端框架來實(shí)現(xiàn)
微前端通常由客戶端工具來支持實(shí)現(xiàn)(聽上去好有道理),有許多支持客戶端開發(fā)微前端的實(shí)現(xiàn)工具,包括:Piral,Open Components,qiankun,Luigi,F(xiàn)rint.js等。其中qiankun是螞蟻金服開發(fā)的。
在客戶端還可以通過輔助庫的方式來實(shí)現(xiàn),輔助庫可以為共享依賴項(xiàng),路由事件或不同的微前端及其生命周期來提供一些基礎(chǔ)架構(gòu)。
下面的一個(gè)示例是通過諸如導(dǎo)入映射或打包特定塊等機(jī)制處理共享依賴關(guān)系。
相關(guān)的工具有Webpack5 Module Federation,Siteless,Single SPA,Postal.js等
通過服務(wù)器端實(shí)現(xiàn)
微前端并非只能在客戶端來實(shí)現(xiàn),類似于服務(wù)端渲染,同樣可以通過服務(wù)端來實(shí)現(xiàn)。
服務(wù)端微前端的支持工具有:Mosaic,PuzzleJs,Podium,Micromono等。
好了,說了這么多我相信你是一臉懵逼的,到底怎么實(shí)現(xiàn)的?我們拋開架構(gòu)不說,來看看到底如何實(shí)現(xiàn)吧。
四、運(yùn)行時(shí)微前端的具體實(shí)現(xiàn)方式
Iframe
iframes是可以在html中嵌入另一個(gè)HTML。下面就是用iframe實(shí)現(xiàn)微前端的一個(gè)例子:
<!DOCTYPE html><html><body><iframe src="http://localhost:3006" width="600" height="900"> <p>Your browser does not support iframes.</p></iframe><iframe src="http://localhost:3007" width="600" height="900"> <p>Your browser does not support iframes.</p></iframe></body></html>
如果不考慮體驗(yàn)問題,iframe 幾乎是最完美的微前端解決方案了。iframe 提供了瀏覽器原生的隔離方案,不論是樣式隔離、js 隔離這類問題統(tǒng)統(tǒng)都能被完美解決。但它的最大問題也在于他的隔離性無法被突破,導(dǎo)致應(yīng)用間上下文無法被共享,隨之帶來的開發(fā)體驗(yàn)、產(chǎn)品體驗(yàn)的問題。這里的主要問題包括:
url 不同步。瀏覽器刷新 iframe url 狀態(tài)丟失、后退前進(jìn)按鈕無法使用。
UI 不同步,DOM 結(jié)構(gòu)不共享。
全局上下文完全隔離,內(nèi)存變量不共享。
慢。每次子應(yīng)用進(jìn)入都需要次瀏覽器上下文的重建、資源重新加載。
所以雖然使用iframe可以實(shí)現(xiàn)遠(yuǎn)程加載的效果,但是因?yàn)檫@些限制,很少會(huì)有應(yīng)用會(huì)使用。
Nginx路由
利用Ngix路由,我們可以把不同的請(qǐng)求路由到不同的微前端的應(yīng)用。
例如Nginx的路由能力,在前端可以動(dòng)態(tài)請(qǐng)求不同的后端應(yīng)用,而每一個(gè)后端應(yīng)用獨(dú)立運(yùn)行,前端可以把這些不同的后端應(yīng)用加載,編排在一起。下面的代碼是一個(gè)Nginx的配置,customers/users/admins分別表示了三個(gè)不同的應(yīng)用,前端通過路由來加載位于不同服務(wù)的后端應(yīng)用。
worker_processes 4;events { worker_connections 1024; }http { server { listen 80; root /usr/share/nginx/html; include /etc/nginx/mime.types; location /app1 { try_files $uri app1/index.html; }
location /app2 { try_files $uri app2/index.html; }
location /app3 { try_files $uri app3/index.html; } }}
無論你采用哪一種的微前端架構(gòu),Nginx方向代理或者其它的API網(wǎng)關(guān)的解決方案都能夠提供方便靈活的后端路由功能。但是通過這種方式,需要定義一個(gè)通用可擴(kuò)展的路由規(guī)則,否則當(dāng)引入新的應(yīng)用的時(shí)候,還需要修改Nginx的路由配置,那就很不方便了。
Webpack 5 Module Federation
Webpack5 的Module Federation是一個(gè)令人興奮的革新,它能夠很方便的支持微前端的構(gòu)建。模塊聯(lián)合允許JavaScript應(yīng)用程序從另一個(gè)應(yīng)用程序動(dòng)態(tài)加載代碼,并在此過程中能共享依賴關(guān)系。如果使用Module Federation的應(yīng)用程序不具有聯(lián)合代碼所需的依賴關(guān)系,則Webpack將從該聯(lián)合構(gòu)建源中下載缺少的依賴關(guān)系。
在Module Federation的上下文中,啟動(dòng)代碼是一種將運(yùn)行時(shí)代碼附加到遠(yuǎn)程容器啟動(dòng)序列的實(shí)施策略。這真的很有用,因?yàn)橥ㄟ^Hook無法訪問ModuleFederation及其運(yùn)行時(shí),無法對(duì)其進(jìn)行擴(kuò)展或添加一行代碼,這些代碼可以像動(dòng)態(tài)設(shè)置遠(yuǎn)程容器的公共路徑那樣進(jìn)行操作。這在普通的webpack應(yīng)用程序中是微不足道的,但是在一個(gè)無法訪問的自定義運(yùn)行時(shí)容器中卻很難做到,該容器為模塊聯(lián)合遠(yuǎn)程編排提供了動(dòng)力。簡(jiǎn)單來說,Module Federation注入一段運(yùn)行時(shí)的代碼來負(fù)責(zé)加載和編排遠(yuǎn)程的應(yīng)用代碼,并能夠管理和加載遠(yuǎn)程應(yīng)用的依賴。
下面是一個(gè)對(duì)應(yīng)的例子:
module.exports = { mode: 'development', devServer: { port: 8080, }, plugins: [ new ModuleFederationPlugin({ name: 'container', remotes: { microFrontEnd1: 'microFrontEnd1@http://localhost:8081/remoteEntry.js', microFrontEnd2: 'microFrontEnd2@http://localhost:8082/remoteEntry.js', }, }) ]};
上面的代碼是微前端的容器端的配置,容器負(fù)責(zé)加載其它遠(yuǎn)程應(yīng)用的代碼。這個(gè)例子里,它加載了兩個(gè)遠(yuǎn)程應(yīng)用。
module.exports = { mode: 'development', devServer: { port: 8081, }, plugins: [ new ModuleFederationPlugin({ name: 'microFrontEnd1', filename: 'remoteEntry.js', exposes: { './MicroFrontEnd1Index': './src/index', }, }), ]};
每一個(gè)微前端的Webpack配置如上。
利用ModuleFederationPlugin,remote可以用來加載遠(yuǎn)端的應(yīng)用,而Expose可以把自己的組件暴露為遠(yuǎn)端組件。
在container中,只需要調(diào)用以下的代碼來加載遠(yuǎn)端組件。
import 'microFrontEnd1/MicroFrontEnd1Index';import 'microFrontEnd2/MicroFrontEnd2Index';
Module Federation的加載過程如上圖所示:
localhost 加載index.html
main.js 是Module Federation的核心的編排代碼,負(fù)責(zé)加載遠(yuǎn)程組件。
remoteEntry.js 是Module Federation暴露的遠(yuǎn)程組件的代碼
src_ 是打包后的代碼,其中 bootstrap_js是容器側(cè)的代碼,index_js是微前端側(cè)的代碼。
Module Federation實(shí)現(xiàn)了類似動(dòng)態(tài)鏈接庫的能力,可以在運(yùn)行時(shí)加載遠(yuǎn)程代碼,遠(yuǎn)程代碼本質(zhì)上是一個(gè)加載在window上的全局變量,Module Federation可以幫助解決依賴的問題。Javascrip作為上古語言,沒有提供依賴管理,導(dǎo)致留給各路大神各種發(fā)揮的空間。
Module Federation的缺點(diǎn)就是依賴Webpack 5,包直接掛載為全局變量。
EMP微前端是基于Module Federation的微前端解決方案。
Single SPA
單頁面應(yīng)用是當(dāng)今為Web應(yīng)用的主流,區(qū)別于傳統(tǒng)的多頁面應(yīng)用,整個(gè)SPA只有一個(gè)頁面,其內(nèi)容都是通過Javascript的功能來加載。
SPA是一個(gè)Web應(yīng)用程序,僅包含一個(gè)HTML頁面。提供動(dòng)態(tài)更新,它允許在不刷新頁面的情況下與頁面進(jìn)行交互。利用單頁應(yīng)用程序,可以顯著降低服務(wù)器負(fù)載并提高加載速度,從而獲得更好的用戶體驗(yàn),因?yàn)镾PA僅在先前加載整個(gè)頁面時(shí)才按需導(dǎo)入數(shù)據(jù)。
除了開發(fā)復(fù)雜,對(duì)于SEO不友好,但頁面應(yīng)用的最大技術(shù)缺陷是URL不適合共享,因?yàn)镾PA只有一個(gè)地址。
single-spa是一個(gè)框架,用于將前端應(yīng)用程序中的多個(gè)JavaScript微前端組合在一起。
使用single-spa構(gòu)建前端可以帶來很多好處,例如:
在同一頁面上使用多個(gè)框架而無需刷新頁面(React,AngularJS,Angular,Embe)
獨(dú)立部署微前端
使用新框架編寫代碼,而無需重寫現(xiàn)有應(yīng)用程序
延遲加載代碼可縮短初始加載時(shí)間
single-spa應(yīng)用程序包含以下內(nèi)容:
single-spa根配置,用于呈現(xiàn)HTML頁面和注冊(cè)應(yīng)用程序的JavaScript。每個(gè)應(yīng)用程序都注冊(cè)了以下三項(xiàng)內(nèi)容:name,加載應(yīng)用程序代碼的函數(shù),確定應(yīng)用程序何時(shí)處于活動(dòng)狀態(tài)/非活動(dòng)狀態(tài)的函數(shù),
打包成模塊的單頁應(yīng)用程序的應(yīng)用程序。每個(gè)應(yīng)用程序必須知道如何從DOM引導(dǎo),安裝和卸載自身。傳統(tǒng)SPA和Single-SPA應(yīng)用程序之間的主要區(qū)別在于,它們必須能夠與其他應(yīng)用程序共存,因?yàn)樗鼈兏髯詻]有自己的HTML頁面。例如,React或Angular SPA應(yīng)用程序。處于活動(dòng)狀態(tài)時(shí),他們可以偵聽url路由事件并將內(nèi)容放在DOM上。處于不活動(dòng)狀態(tài)時(shí),它們不偵聽url路由事件,并且已從DOM中完全刪除。
Single-SPA注冊(cè)的應(yīng)用程序擁有普通SPA所具有的所有功能,只是它沒有HTML頁面。SPA包含許多已注冊(cè)的應(yīng)用程序,每個(gè)應(yīng)用程序都有其自己的框架。已注冊(cè)的應(yīng)用程序具有其自己的客戶端路由和它們自己的框架/庫。它們呈現(xiàn)自己的HTML,并且在安裝時(shí)有完全的自由去做他們想做的任何事情。掛載的概念是指已注冊(cè)的應(yīng)用程序是否正在將內(nèi)容放在DOM上。決定是否掛載已注冊(cè)應(yīng)用程序的是其活動(dòng)功能。每當(dāng)未掛載已注冊(cè)的應(yīng)用程序時(shí),它都應(yīng)保持完全休眠狀態(tài)直到掛載。
Single SPA的樣例代碼如下:
1. 微前端代碼:
import React from "react";import ReactDOM from "react-dom";import singleSpaReact from "single-spa-react";import Root from "./root.component";const lifecycles = singleSpaReact({ React, ReactDOM, rootComponent: Root, errorBoundary(err, info, props) { // Customize the root error boundary for your microfrontend here. return null; },});export const { bootstrap, mount, unmount } = lifecycles;
Single SPA的微前端是純的JS組件,不包含HTML,需要通過容器來加載。
2.容器的Root Config
在容器側(cè),需要通過Import Map或者Webpack來定義遠(yuǎn)程組件,并注冊(cè)遠(yuǎn)程應(yīng)用。
{ "imports": { "@naughty/root-config": "//localhost:9000/naughty-root-config.js", "@naughty/app": "//localhost:8500/naughty-app.js", "react": "https://cdn.jsdelivr.net/npm/react@16.13.1/umd/react.production.min.js", "react-dom": "https://cdn.jsdelivr.net/npm/react-dom@16.13.1/umd/react-dom.production.min.js" }}
容器側(cè)的HTML文件使用import map來定義遠(yuǎn)程依賴,其中root-config是編排代碼,負(fù)責(zé)遠(yuǎn)程應(yīng)用的注冊(cè)和加載。同時(shí)需要定義所有共享的依賴,這里例子中是react和react-dom
import { registerApplication, start } from "single-spa";registerApplication({ name: "@single-spa/welcome", app: () => System.import( "https://unpkg.com/single-spa-welcome/dist/single-spa-welcome.js" ), activeWhen: ["/welcome"],});registerApplication( '@naughty/app', () => System.import('@naughty/app'), location => location.pathname.startsWith('/app'));start({ urlRerouteOnly: true,});
在root-config中,我們注冊(cè)了兩個(gè)遠(yuǎn)程應(yīng)用,使用不同的url來加載。/welcome會(huì)加載welcome應(yīng)用,而/app會(huì)加載我們的app應(yīng)用。
Single SPA的核心是利用不同的URL路由來加載遠(yuǎn)程組件,它可以和Webpack(打包時(shí)構(gòu)建依賴)或者Import Map(運(yùn)行時(shí)使用瀏覽器導(dǎo)入依賴)一起工作。注意,不要在你的微前端中混用兩種依賴機(jī)制。
Single SPA還提供一個(gè)layout 引擎,可以幫助你快速的構(gòu)建微前端。
相比Module Federation,Single SPA的代碼和生命周期的管理更清楚,提供清晰的接口,缺點(diǎn)是共享的依賴需要手工通過import map來管理。
要做一個(gè)好的微前端因?yàn)槭芟抻跒g覽器和JS的一些特性,并不容易。除了我們今天分享的內(nèi)容,還面臨著諸多的挑戰(zhàn):如何解決css/js的沖突,使得組件和應(yīng)用完全隔離;如何解決不同應(yīng)用間的通信;如何處理路由;如何保證UI風(fēng)格的統(tǒng)一等等。
五、微前端的問題和缺點(diǎn)
講了這么多的優(yōu)點(diǎn)和實(shí)現(xiàn),那么微前端是不是解決前端開發(fā)問題的銀彈呢?當(dāng)然不是。所有的架構(gòu)都是取舍和權(quán)衡,這個(gè)世界上并不存在銀彈,微前端架構(gòu)和微服務(wù)一樣也存在他的弊端,單體架構(gòu)未必就差。
1. 微前端的構(gòu)建通常比較復(fù)雜,從工具,打包,到部署,微前端都是更為復(fù)雜的存在,天下沒有免費(fèi)的午餐,對(duì)于小型項(xiàng)目,它的成本太高。
2. 每個(gè)團(tuán)隊(duì)可以使用不同的框架,這個(gè)聽上去很美,但是實(shí)際操作起來,除了要支持歷史遺留的應(yīng)用,它的意義不大。同時(shí)也為帶來體驗(yàn)上的問題?梢赃h(yuǎn)程加載不同的框架代碼是一回事,把它們都用好是另一回事。
3. 性能上來看,如果優(yōu)化得不好微前端的性能可能會(huì)存在問題,至少微前端框架是額外的一層加載。如果不同的微前端使用了不同的框架,那么每一個(gè)框架都需要額外的加載。
微前端架構(gòu)還在發(fā)展之中,本文提到的iframe/nginx/module federation/single-spa只是諸多解決方案中的一小部分,前端的發(fā)展變化和生態(tài)系統(tǒng)實(shí)在是豐富,其他的方案諸如umd/乾坤,Piral,open comonent等等。當(dāng)使用你也可以選擇標(biāo)準(zhǔn)的Web Component或者ES Modules來構(gòu)建微前端,但是這些標(biāo)準(zhǔn)的瀏覽器支持不是特別好,這個(gè)是前端開發(fā)永遠(yuǎn)的痛。(詛咒IE)
大家對(duì)于微前端有什么想法或者問題,歡迎一起討論。
關(guān)于作者:陶剛,Splunk資深軟件工程師,架構(gòu)師,畢業(yè)于北京郵電大學(xué),現(xiàn)在在溫哥華負(fù)責(zé)Splunk機(jī)器學(xué)習(xí)云平臺(tái)的開發(fā),曾經(jīng)就職于SAP,EMC,Lucent等企業(yè),擁有豐富的企業(yè)應(yīng)用軟件開發(fā)經(jīng)驗(yàn),熟悉軟件開發(fā)的各種技術(shù),平臺(tái)和開發(fā)過程,在商務(wù)智能,機(jī)器學(xué)習(xí),數(shù)據(jù)可視化,數(shù)據(jù)采集,網(wǎng)絡(luò)管理等領(lǐng)域都有涉及。
發(fā)表評(píng)論
請(qǐng)輸入評(píng)論內(nèi)容...
請(qǐng)輸入評(píng)論/評(píng)論長(zhǎng)度6~500個(gè)字
最新活動(dòng)更多
-
即日-11.13立即報(bào)名>>> 【在線會(huì)議】多物理場(chǎng)仿真助跑新能源汽車
-
11月20日火熱報(bào)名中>> 2024 智能家居出海論壇
-
11月28日立即報(bào)名>>> 2024工程師系列—工業(yè)電子技術(shù)在線會(huì)議
-
12月19日立即報(bào)名>> 【線下會(huì)議】OFweek 2024(第九屆)物聯(lián)網(wǎng)產(chǎn)業(yè)大會(huì)
-
即日-12.26火熱報(bào)名中>> OFweek2024中國智造CIO在線峰會(huì)
-
即日-2025.8.1立即下載>> 《2024智能制造產(chǎn)業(yè)高端化、智能化、綠色化發(fā)展藍(lán)皮書》
推薦專題
- 1 【一周車話】沒有方向盤和踏板的車,你敢坐嗎?
- 2 特斯拉發(fā)布無人駕駛車,還未迎來“Chatgpt時(shí)刻”
- 3 特斯拉股價(jià)大跌15%:Robotaxi離落地還差一個(gè)蘿卜快跑
- 4 馬斯克給的“驚喜”夠嗎?
- 5 打完“價(jià)格戰(zhàn)”,大模型還要比什么?
- 6 馬斯克致敬“國產(chǎn)蘿卜”?
- 7 神經(jīng)網(wǎng)絡(luò),誰是盈利最強(qiáng)企業(yè)?
- 8 比蘋果偉大100倍!真正改寫人類歷史的智能產(chǎn)品降臨
- 9 諾獎(jiǎng)進(jìn)入“AI時(shí)代”,人類何去何從?
- 10 Open AI融資后成萬億獨(dú)角獸,AI人才之爭(zhēng)開啟
- 高級(jí)軟件工程師 廣東省/深圳市
- 自動(dòng)化高級(jí)工程師 廣東省/深圳市
- 光器件研發(fā)工程師 福建省/福州市
- 銷售總監(jiān)(光器件) 北京市/海淀區(qū)
- 激光器高級(jí)銷售經(jīng)理 上海市/虹口區(qū)
- 光器件物理工程師 北京市/海淀區(qū)
- 激光研發(fā)工程師 北京市/昌平區(qū)
- 技術(shù)專家 廣東省/江門市
- 封裝工程師 北京市/海淀區(qū)
- 結(jié)構(gòu)工程師 廣東省/深圳市