新聞中心
想做好前端很難,做出可擴(kuò)展的前端,從而讓多個(gè)團(tuán)隊(duì)可以同時(shí)投身于一項(xiàng)復(fù)雜的大型產(chǎn)品項(xiàng)目就更難了。本文將介紹前端領(lǐng)域最近的一項(xiàng)變革:?jiǎn)误w前端架構(gòu)正在過(guò)渡到許多較小、較易管理的前端架構(gòu)。我們還會(huì)展示這種新的體系結(jié)構(gòu)怎樣提升前端團(tuán)隊(duì)的效率和表現(xiàn)。除了討論這種新趨勢(shì)的好處與代價(jià)外,我們還將介紹一些可行的實(shí)現(xiàn)方案,并深入分析一個(gè)完整的微前端應(yīng)用案例。

創(chuàng)新互聯(lián)建站服務(wù)緊隨時(shí)代發(fā)展步伐,進(jìn)行技術(shù)革新和技術(shù)進(jìn)步,經(jīng)過(guò)十多年的發(fā)展和積累,已經(jīng)匯集了一批資深網(wǎng)站策劃師、設(shè)計(jì)師、專業(yè)的網(wǎng)站實(shí)施團(tuán)隊(duì)以及高素質(zhì)售后服務(wù)人員,并且完全形成了一套成熟的業(yè)務(wù)流程,能夠完全依照客戶要求對(duì)網(wǎng)站進(jìn)行成都網(wǎng)站制作、成都做網(wǎng)站、外貿(mào)營(yíng)銷網(wǎng)站建設(shè)、建設(shè)、維護(hù)、更新和改版,實(shí)現(xiàn)客戶網(wǎng)站對(duì)外宣傳展示的首要目的,并為客戶企業(yè)品牌互聯(lián)網(wǎng)化提供全面的解決方案。
微服務(wù)近年來(lái)大受歡迎,許多組織轉(zhuǎn)向了微服務(wù)以克服大型單體后端架構(gòu)的局限。但雖然微服務(wù)在服務(wù)端很流行,很多企業(yè)在前端代碼庫(kù)上仍然在沿用問(wèn)題多多的單體架構(gòu)。
也許你想構(gòu)建一個(gè)漸進(jìn)式或響應(yīng)式的 Web 應(yīng)用,但卻找不到一種將這些功能集成進(jìn)現(xiàn)有代碼中的簡(jiǎn)單途徑;也許你想嘗試 JavaScript 語(yǔ)言的新功能(或者是其他可以編譯為 JS 的某種語(yǔ)言),但你卻無(wú)法將關(guān)鍵的構(gòu)建工具融入已有的構(gòu)建流程;或者你只是想擴(kuò)展開發(fā)流程,讓多個(gè)團(tuán)隊(duì)可以同時(shí)開發(fā)一種產(chǎn)品,但現(xiàn)有單體架構(gòu)中的耦合度與復(fù)雜性讓團(tuán)隊(duì)間的合作變得磕磕絆絆。這些都是很現(xiàn)實(shí)的問(wèn)題,都會(huì)影響你們向客戶交付高質(zhì)量體驗(yàn)的能力。
微前端的定義
最近業(yè)界越來(lái)越關(guān)注復(fù)雜的現(xiàn)代化 Web 開發(fā)需要怎樣的整體架構(gòu)和組織結(jié)構(gòu)這個(gè)問(wèn)題。于是我們開始看到單體前端正在分解為更小、更簡(jiǎn)單的模塊,這些模塊可以各自獨(dú)立開發(fā)、測(cè)試和部署,而它們組合在一起仍然對(duì)客戶表現(xiàn)為一件單一完整的產(chǎn)品。我們將這種技術(shù)稱為 微前端,其定義為:
“微前端是一種架構(gòu)風(fēng)格,其中眾多獨(dú)立交付的前端應(yīng)用組合成一個(gè)大型整體?!?/p>
我們認(rèn)為微前端的主要好處有:
- 更小,更緊密且更易維護(hù)的代碼庫(kù)。
- 組織更具擴(kuò)展能力,其團(tuán)隊(duì)更加獨(dú)立自治。
- 能夠以更加增量式的風(fēng)格來(lái)升級(jí)、更新前端,甚至重寫部分前端代碼。
這些核心優(yōu)勢(shì)與微服務(wù)的優(yōu)勢(shì)基本一致,這也不是什么巧合。
當(dāng)然,軟件架構(gòu)領(lǐng)域沒(méi)有免費(fèi)的午餐:一切都要付出代價(jià)。一些微前端實(shí)現(xiàn)可能導(dǎo)致重復(fù)依賴,使用戶不得不下載更多內(nèi)容。此外,大幅提升的團(tuán)隊(duì)自治水平可能會(huì)讓各個(gè)團(tuán)隊(duì)的工作愈加分裂。只不過(guò)我們認(rèn)為這些風(fēng)險(xiǎn)都能控制在合理水平上,微前端終究還是利大于弊的。
好處
我們不會(huì)從具體的技術(shù)方法或?qū)嵤┘?xì)節(jié)角度來(lái)定義微前端,而是重點(diǎn)關(guān)注它的屬性和好處。
增量升級(jí)
對(duì)于許多組織來(lái)說(shuō),追求增量升級(jí)就是他們邁向微前端的***步。對(duì)他們來(lái)說(shuō),老式的大型單體前端要么是用老舊的技術(shù)棧打造的,要么就充斥著匆忙寫成的代碼,已經(jīng)到了該重寫整個(gè)前端的時(shí)候了。一次性重寫整個(gè)系統(tǒng)風(fēng)險(xiǎn)很大,我們更傾向一點(diǎn)一點(diǎn)換掉老的應(yīng)用,同時(shí)在不受單體架構(gòu)拖累的前提下為客戶不斷提供新功能。
為了做到這一點(diǎn),解決方案往往就是微前端架構(gòu)了。一旦某個(gè)團(tuán)隊(duì)掌握了在幾乎不影響舊世界的同時(shí)為生產(chǎn)環(huán)境引入新功能的訣竅,其他團(tuán)隊(duì)就會(huì)紛紛效仿?,F(xiàn)有代碼仍然需要繼續(xù)維護(hù)下去,但在某些情況下還要繼續(xù)添加新功能,現(xiàn)在總算有了解決方案。
到***,我們就能更隨心所欲地改動(dòng)產(chǎn)品的各個(gè)部分,并逐漸升級(jí)我們的架構(gòu)、依賴關(guān)系和用戶體驗(yàn)。當(dāng)主框架發(fā)生重大變化時(shí)每個(gè)微前端模塊都可以按需升級(jí),不需要整體下線或一次性升級(jí)所有內(nèi)容。如果我們想要嘗試新的技術(shù)或互動(dòng)模式,也能在隔離度更好的環(huán)境下做試驗(yàn)。
簡(jiǎn)潔、解耦的代碼庫(kù)
微前端體系下,每個(gè)小模塊的代碼庫(kù)要比一個(gè)單體前端的代碼庫(kù)小很多。對(duì)開發(fā)者來(lái)說(shuō)這些較小的代碼庫(kù)處理起來(lái)更簡(jiǎn)單方便。而且微前端還能避免無(wú)關(guān)組件之間不必要的耦合,讓代碼更簡(jiǎn)潔。我們可以在應(yīng)用的限界上下文(詳見下方鏈接)處劃出更明顯的界限,更好地避免無(wú)意間造成的這類耦合問(wèn)題。
當(dāng)然,只靠架構(gòu)更迭本身(比如說(shuō)“我們改成微前端吧”)并不能自動(dòng)為以往的優(yōu)質(zhì)代碼生成替代品。我們要做的是設(shè)法讓糟糕的決策難以露頭,而讓正確的決策暢通無(wú)阻,從而進(jìn)入邁向成功的良性循環(huán)。例如,現(xiàn)在很難跨越限界上下文共享域模型,所以開發(fā)者就不太可能這樣做了。類似地,微前端會(huì)讓開發(fā)者更審慎地把握數(shù)據(jù)和事件在應(yīng)用的各個(gè)部分之間流動(dòng)的方式,其實(shí)就算沒(méi)有微前端我們本來(lái)也應(yīng)該這樣做的!
獨(dú)立部署
就像微服務(wù)一樣,微前端的一大優(yōu)勢(shì)就是可獨(dú)立部署的能力。這種能力會(huì)縮減每次部署涉及的范圍,從而降低了風(fēng)險(xiǎn)。不管你的前端代碼是在哪里托管,怎樣托管,各個(gè)微前端都應(yīng)該有自己的持續(xù)交付管道;這些管道可以將微前端構(gòu)建、測(cè)試并部署到生產(chǎn)環(huán)境中。我們?cè)诓渴鸶鱾€(gè)微前端時(shí)幾乎不用考慮其他代碼庫(kù)或管道的狀態(tài);就算舊的單體架構(gòu)采用了固定、手動(dòng)的按季發(fā)布周期,或者隔壁的團(tuán)隊(duì)在他們的主分支里塞進(jìn)了一個(gè)半成品或失敗的功能,也不影響我們的工作。如果某個(gè)微前端已準(zhǔn)備好投入生產(chǎn),那么它就能順利變?yōu)楫a(chǎn)品,且這一過(guò)程完全由開發(fā)和維護(hù)它的團(tuán)隊(duì)主導(dǎo)。
自治團(tuán)隊(duì)
解藕代碼庫(kù)、分離發(fā)布周期還能帶來(lái)一個(gè)高層次的好處,那就是大幅提升團(tuán)隊(duì)的獨(dú)立性;一支獨(dú)立的團(tuán)隊(duì)可以自主完成從產(chǎn)品構(gòu)思到最終發(fā)布的完整流程,有足夠的能力獨(dú)立向客戶交付價(jià)值,從而可以更快、更高效地工作。為了實(shí)現(xiàn)這一目標(biāo)需要圍繞垂直業(yè)務(wù)功能,而非技術(shù)功能來(lái)打造團(tuán)隊(duì)。一種簡(jiǎn)單的方法是根據(jù)最終用戶將看到的內(nèi)容來(lái)劃分產(chǎn)品模塊,讓每個(gè)微前端都封裝應(yīng)用的某個(gè)頁(yè)面,并分配給一個(gè)團(tuán)隊(duì)完整負(fù)責(zé)。相比圍繞技術(shù)或“橫向”問(wèn)題(如樣式、表單或驗(yàn)證)打造的團(tuán)隊(duì)相比,這種團(tuán)隊(duì)能有更高的凝聚力。
小結(jié)
簡(jiǎn)而言之,微前端是將龐大復(fù)雜的整體分割為更小、更易于管理的模塊,然后明確它們之間的依賴關(guān)系。我們的技術(shù)決策、代碼庫(kù)、團(tuán)隊(duì)和發(fā)布流程都應(yīng)該彼此獨(dú)立,無(wú)需過(guò)多協(xié)調(diào)工作就能自主運(yùn)行并發(fā)展。
案例
假設(shè)要做一個(gè)食品外賣的網(wǎng)站。乍一看這種網(wǎng)站好像很好做,但想要做好需要在諸多細(xì)節(jié)上下足功夫:
- 應(yīng)該有一個(gè)引導(dǎo)頁(yè)面,讓顧客瀏覽并搜索餐館。顧客應(yīng)該能按照一系列參數(shù)(包括價(jià)格、菜品或訂購(gòu)歷史等)來(lái)搜索并過(guò)濾餐館。
- 每家餐館都要有自己的頁(yè)面,頁(yè)面中要展示菜單,允許客戶自主選餐,還要有折扣、套餐和特殊要求選項(xiàng)。
- 顧客應(yīng)該有自己的主頁(yè),可以用來(lái)查看訂單歷史、跟蹤外賣進(jìn)度并自定義付款選項(xiàng)
每個(gè)頁(yè)面都非常復(fù)雜,都應(yīng)該分配一個(gè)專門團(tuán)隊(duì)來(lái)負(fù)責(zé),并且每個(gè)團(tuán)隊(duì)都應(yīng)該有足夠的獨(dú)立性。各個(gè)團(tuán)隊(duì)都應(yīng)該能獨(dú)立開發(fā)、測(cè)試、部署和維護(hù)自己的代碼,而不會(huì)與其他團(tuán)隊(duì)發(fā)生沖突或需要其他團(tuán)隊(duì)配合。但在客戶這里,整個(gè)網(wǎng)站仍然應(yīng)該是一個(gè)無(wú)縫的整體。
下面我們就會(huì)圍繞這個(gè)案例來(lái)展示代碼與場(chǎng)景示例。
集成方法
前文對(duì)微前端的定義相當(dāng)松散,所以有很多方法都可以劃入這個(gè)范疇。本節(jié)將展示一些示例并討論它們的優(yōu)劣。這些方法在架構(gòu)上有共通之處——通常應(yīng)用中的每個(gè)頁(yè)面都有一個(gè)微前端,還有一個(gè) 容器應(yīng)用,它有以下功能:
- 呈現(xiàn)常見的頁(yè)面元素,如頁(yè)眉和頁(yè)腳。
- 解決了身份認(rèn)證和跳轉(zhuǎn)等跨領(lǐng)域問(wèn)題。
- 在頁(yè)面上集成多個(gè)微前端,并告訴各個(gè)微前端該何時(shí)何地呈現(xiàn)自己。
服務(wù)器端模板組合
先來(lái)介紹一種非常新穎的前端開發(fā)方法——就是在服務(wù)器上使用多個(gè)模板或片段呈現(xiàn) HTML。首先我們要有一個(gè) index.html,其中包含所有常見的頁(yè)面元素;然后使用服務(wù)器端包含從 HTML 片段文件中插入的特定頁(yè)面內(nèi)容:
Feed me Feed me
我們使用 Nginx 提供此文件,通過(guò)匹配正在請(qǐng)求的 URL 來(lái)配置 $PAGE 變量:
- server {
- listen 8080;
- server_name localhost;
- root /usr/share/nginx/html;
- index index.html;
- ssi on;
- # Redirect / to /browse
- rewrite ^/$ http://localhost:8080/browse redirect;
- # Decide which HTML fragment to insert based on the URL
- location /browse {
- set $PAGE 'browse';
- }
- location /order {
- set $PAGE 'order';
- }
- location /profile {
- set $PAGE 'profile'
- }
- # All locations should render through index.html
- error_page 404 /index.html;
- }
這是相當(dāng)標(biāo)準(zhǔn)的服務(wù)器端組合方法。它之所以可以算作微前端,是因?yàn)槲覀兛梢杂纱藖?lái)分割代碼,讓每部分代碼代表一個(gè)自包含的域概念,并由一個(gè)獨(dú)立的團(tuán)隊(duì)負(fù)責(zé)。這里沒(méi)有展示各個(gè) HTML 片段文件最終如何在 Web 服務(wù)器上呈現(xiàn),實(shí)際上它們都有自己的部署管道,改動(dòng)某個(gè)頁(yè)面并不會(huì)影響其他內(nèi)容。
想要更高獨(dú)立性的話,可以為每個(gè)微前端單獨(dú)安排一個(gè)服務(wù)器負(fù)責(zé)呈現(xiàn)和服務(wù),再安排一個(gè)服務(wù)器專門向其他服務(wù)器發(fā)出請(qǐng)求。如果能緩存好各個(gè)響應(yīng)就不會(huì)增大延遲。
這個(gè)例子說(shuō)明微前端不一定是一種新技術(shù),也不一定很復(fù)雜。只要我們的設(shè)計(jì)決策能為代碼庫(kù)和團(tuán)隊(duì)賦予更多自主權(quán),那么不管怎樣的技術(shù)棧都能為我們帶來(lái)類似的收益。
構(gòu)建時(shí)集成
還有一種方法是將每個(gè)微前端作為一個(gè)包來(lái)發(fā)布,并讓容器應(yīng)用將它們?nèi)孔鳛閹?kù)依賴包含進(jìn)去。下面展示了容器的 package.json 查找本文示例應(yīng)用的方法:
- {
- "name": "@feed-me/container",
- "version": "1.0.0",
- "description": "A food delivery web app",
- "dependencies": {
- "@feed-me/browse-restaurants": "^1.2.3",
- "@feed-me/order-food": "^4.5.6",
- "@feed-me/user-profile": "^7.8.9"
- }
- }
這種辦法初看上去挺不錯(cuò)。它通常會(huì)生成一個(gè)可部署的 Javascript 包,允許我們從各種應(yīng)用中刪除常見的重復(fù)依賴。但這意味著我們修改產(chǎn)品的任何部分時(shí)都必須重新編譯和發(fā)布所有微前端。這種 齊步走的發(fā)布流程 在微服務(wù)里已經(jīng)夠讓我們好受了,所以我們強(qiáng)烈建議不要用它來(lái)實(shí)現(xiàn)微前端架構(gòu)。我們好不容易在開發(fā)和測(cè)試階段實(shí)現(xiàn)了解耦和獨(dú)立,可別再在發(fā)布階段又繞回去了。我們得在運(yùn)行時(shí)中也集成微前端。
通過(guò) iframe 在運(yùn)行時(shí)集成
想要在瀏覽器中組合應(yīng)用,一種最簡(jiǎn)單的方法就是用 iframe。iframe 可以輕松地用一系列獨(dú)立的子頁(yè)面構(gòu)建整個(gè)頁(yè)面。它們的樣式和全局變量也能充分隔離,不會(huì)互相干擾。
Feed me! Welcome to Feed me!
就像前文提到的服務(wù)器端包含方法一樣,用 iframe 構(gòu)建頁(yè)面并不是一種激動(dòng)人心的新技術(shù)。但只要我們能精心分割好應(yīng)用并組建好團(tuán)隊(duì),那么用 iframe 就能實(shí)現(xiàn)前面提到的一系列好處。
很多人不喜歡 iframe,它也的確有一些缺陷。上面提到的簡(jiǎn)單隔離方式確實(shí)降低了它的靈活性。用 iframe 在應(yīng)用的各個(gè)部分之間構(gòu)建集成可能會(huì)很困難,從而讓路由、歷史記錄和深層鏈接變得更加復(fù)雜;它還會(huì)影響頁(yè)面的響應(yīng)速度。
通過(guò) JavaScript 在運(yùn)行時(shí)集成
這個(gè)方法非常靈活,應(yīng)用廣泛。每個(gè)微前端都使用


咨詢
建站咨詢