作為前端開發人員,這些年來你一直在開發單體應用,即使你已經知道這是一個不好的做法。 您將程式碼劃分為元件,使用 require 或 import 並將package.json中定義的npm包或已安裝的子git倉庫新增到專案中,但最終構建了一個整體。 是時候改變它了。
為什麼你的程式碼是一個單體?
除了已經實現了微前端的應用之外,所有前端應用本質上都是單一的應用。 原因是如果您正在使用 React 庫進行開發,並且如果您有兩個團隊,則兩個團隊都應該使用相同的React 庫,並且兩個團隊應該在部署時保持同步,並且在程式碼合併期間始終會發生衝突。 它們沒有完全分離,很可能它們維護著相同的倉庫並具有相同的構建系統。 單體應用的退出被標誌為微服務的出現。 但是它適用於後端!?
什麼是微服務?
對於微服務,一般而言最簡單的解釋是,它是一種開發技術,允許開發人員為平臺的不同部分進行獨立部署,而不會損害其他部分。 獨立部署的能力允許他們構建孤立或鬆散耦合的服務。 為了使這個體系結構更穩定,有一些規則要遵循,可以總結如下:每個服務應該只有一個任務,它應該很小。 所以負責這項服務的團隊應該很小。 關於團隊和專案的規模,James Lewis 和 Martin Fowler 在網際網路上做出的最酷解釋之一如下:
在我們與微服務從業者的對話中,我們看到了一系列服務規模。 報導的最大規模遵循亞馬遜關於Two Pizza Team的概念(即整個團隊可以由兩個比薩餅供給),意味著不超過十幾個人。 在規模較小的規模上,我們已經看到了一個由六人組成的團隊支援六項服務的設定。
我畫了一個簡單的草圖,為整體和微服務提供了直觀的解釋:
從上圖可以理解,微服務中的每個服務都是一個獨立的應用,除了UI。 UI仍然是一體的! 當一個團隊處理所有服務並且公司正在擴充套件時,前端團隊將開始苦苦掙扎並且無法跟上它,這是這種架構的瓶頸。
除了瓶頸之外,這種架構也會導致一些組織問題。 假設公司正在發展並將採用需要 跨職能 小團隊的敏捷開發方法。 在這個常見的例子中,產品所有者自然會開始將故事定義為前端和後端任務,而 跨職能 團隊將永遠不會成為真正的 跨職能 部門。 這將是一個淺薄的泡沫,看起來像一個敏捷的團隊,但它將在內部分開。 關於管理這種團隊的更多資訊將是一項非常重要的工作。 在每個計劃中,如果有足夠的前端任務或者sprint中有足夠的後端任務,則會有一個問題。 為了解決這裡描述的所有問題和許多其他問題,幾年前出現了微前端的想法並且開始迅速普及。
解決微服務中的瓶頸問題:Micro Frontends?
解決方案實際上非常明顯,採用了多年來為後端服務工作的相同原則:將前端整體劃分為小的UI片段。 但UI與服務並不十分相似,它是終端使用者與產品之間的介面,應該是一致且無縫的。 更重要的是,在單頁面應用時代,整個應用在客戶端的瀏覽器上執行。 它們不再是簡單的HTML檔案,相反,它們是複雜的軟體,達到了非常複雜的水平。 現在我覺得微型前端的定義是必要的:
Micro Frontends背後的想法是將網站或Web應用視為獨立團隊擁有的功能組合。 每個團隊都有一個獨特的業務或任務領域,做他們關注和專注的事情。團隊是跨職能的,從資料庫到使用者介面開發端到端的功能。(micro-frontends.org)
根據我迄今為止的經驗,對於許多公司來說,直接採用上面提出的架構真的很難。 許多其他人都有巨大的遺留負擔,這使他們無法遷移到新的架構。 出於這個原因,更柔軟的中間解決方案更加靈活,易於採用和安全遷移至關重要。 在更詳細地概述了體系結構後,我將嘗試提供一些體系結構的洞察,該體系結構確認了上述提議並允許更靈活的方式。 在深入瞭解細節之前,我需要建立一些術語。
整體結構和一些術語
讓我們假設我們通過業務功能垂直劃分整體應用結構。 我們最終會得到幾個較小的應用,它們與單體應用具有相同的結構。 但是如果我們在所有這些小型單體應用之上新增一個特殊應用,使用者將與這個新應用進行通訊,它將把每個小應用的舊單體UI組合成一個。 這個新圖層可以命名為拼接圖層,因為它從每個微服務中獲取生成的UI部件,併為終端使用者組合成一個無縫 UI,這將是微前端的最直接實現?
為了更好地理解,我將每個小型單體應用稱為微應用,因為它們都是獨立的應用,而不僅僅是微服務,它們都有UI部件,每個都代表端到端的業務功能。
眾所周知,今天的前端生態系統功能多樣,而且非常複雜。 因此,當實現真正的產品時,這種直接的解決方案還不夠。
要解決的問題
雖然這篇文章只是一個想法,但我開始使用Reddit討論這個想法。 感謝社群和他們的回覆,我可以列出一些需要解決的問題,我將嘗試逐一描述。
當我們擁有一個完全獨立的獨立微應用時,如何建立無縫且一致的UI體驗?
好吧,這個問題沒有靈丹妙藥的答案,但其中一個想法是建立一個共享的UI庫,它也是一個獨立的微應用。 通過這種方式,所有其他微應用將依賴於共享的UI庫微應用。 在這種情況下,我們剛剛建立了一個共享依賴項,我們就殺死了獨立微應用的想法。
另一個想法是在根級共享CSS自定義變數( CSS custom variables )。 此解決方案的優勢在於應用之間的全域性可配置主題。
或者我們可以簡單地在應用團隊之間共享一些SASS變數和混合。 這種方法的缺點是UI元素的重複實現,並且應該對所有微應用始終檢查和驗證類似元素的設計的完整性。
我們如何確保一個團隊不會覆蓋另一個團隊編寫的CSS?
一種解決方案是通過CSS選擇器名稱進行CSS定義,這些名稱由微應用名稱精心選擇。 通過將該範圍任務放在拼接層上將減少開發開銷,但會增加拼接層的責任。
另一種解決方案可以是強制每個微應用成為自定義Web元件(custom web component)。 這個解決方案的優點是瀏覽器完成了範圍設計,但需要付出代價:使用shadow DOM進行伺服器端渲染幾乎是不可能的。 此外,自定義元素沒有100%的瀏覽器支援,特別是IE。
我們應該如何在微應用之間共享全域性資訊?
這個問題指出了關於這個主題的最關注的問題之一,但解決方案非常簡單:HTML 5具有相當強大的功能,大多數前端開發人員都不知道。 例如,自定義事件(custom events) 就是其中之一,它是在微應用中共享資訊的解決方案。
或者,任何共享的pub-sub實現或T39可觀察的實現都可以實現。 如果我們想要一個更復雜的全域性狀態處理程式,我們可以實現共享的微型Redux,通過這種方式我們可以實現更多的相應式架構。
如果所有微應用都是獨立應用,我們如何進行客戶端路由?
這個問題取決於設計的每個實現, 所有主要的現代框架都通過使用瀏覽器歷史狀態在客戶端提供強大的路由機制, 問題在於哪個應用負責路由以及何時。
我目前的實用方法是建立一個共享客戶端路由器,它只負責頂級路由,其餘路由器屬於相應的微應用。 假設我們有 /content/:id
路由定義。 共享路由器將解析 /content
,已解析的路由將傳遞到ContentMicroApp。 ContentMicroApp是一個獨立的伺服器,它將僅使用 /:id
進行呼叫。
我們必須是伺服器端渲染,但是有可能使用微前端嗎?
伺服器端呈現是一個棘手的問題。 如果你正在考慮iframes縫合微應用然後忘記伺服器端渲染。 同樣,拼接任務的Web元件也不比iframe強大。 但是,如果每個微應用能夠在伺服器端呈現其內容,那麼拼接層將僅負責連線伺服器端的HTML片段。
與傳統環境整合至關重要! 但是怎麼樣?
為了整合遺留系統,我想描述我自己的策略,我稱之為“ 漸進式入侵 ”。
首先,我們必須實現拼接層,它應該具有透明代理的功能。 然後我們可以通過宣告一個萬用字元路徑將遺留系統定義為微應用:LegacyMicroApp 。 因此,所有流量都將到達拼接層,並將透明地代理到舊系統,因為我們還沒有任何其他微應用。
下一步將是我們的 第一次逐步入侵 :我們將從LegacyMicroApp中刪除主要導航並用依賴項替換它。 這種依賴關係將是一個使用閃亮的新技術實現的微應用:NavigationMicroApp 。
現在,拼接層將每個路徑解析為 Legacy Micro App ,它將依賴關係解析為 Navigation MicroApp ,並通過連線這兩個來為它們提供服務。
然後通過主導航遵循相同的模式來為引導下一步。
然後我們將繼續從Legacy MicroApp中獲取逐步重複以上操作,直到沒有任何遺漏。
如何編排客戶端,這樣我們每次都不需要重新載入頁面?
拼接層解決了伺服器端的問題,但沒有解決客戶端問題。 在客戶端,在將已貼上的片段作為無縫HTML載入後,我們不需要每次在URL更改時載入所有部分。 因此,我們必須有一些非同步載入片段的機制。 但問題是,這些片段可能有一些依賴關係,這些依賴關係需要在客戶端解決。 這意味著微前端解決方案應提供載入微應用的機制,以及依賴注入的一些機制。
根據上述問題和可能的解決方案,我可以總結以下主題下的所有內容:
客戶端
- 編排
- 路由
- 隔離微應用
- 應用之間通訊
- 微應用UI之間的一致性
服務端
- 服務端渲染
- 路由
- 依賴管理
靈活、強大而簡單的架構
所以,這篇文章還是很值得期待的! 微前端架構的基本要素和要求終於顯現!
在這些要求和關注的指導下,我開始開發一種名為microfe的解決方案。 ?在這裡,我將通過抽象的方式強調其主要元件來描述該專案的架構目標。
它很容易從客戶端開始,它有三個獨立的主幹結構:AppsManager, Loader, Router 和一個額外的MicroAppStore。
AppsManager
AppsManager 是客戶端微應用編排的核心。 AppsManager的主要功能是建立依賴關係樹。 當解決了微應用的所有依賴關係時,它會例項化微應用。
Loader
客戶端微應用編排的另一個重要部分是Loader。 載入器的責任是從伺服器端獲取未解析的微應用。
Router
為了解決客戶端路由問題,我將 Router 引入了 microfe。 與常見的客戶端路由器不同,microf 的功能有限,它不解析頁面而是微應用。 假設我們有一個URL /content/detail/13
和一個ContentMicroApp。 在這種情況下,microfe 將URL解析為 /content/
,它將呼叫ContentMicroApp /detail/13
URL部分。
MicroAppStore
為了解決微應用到微應用客戶端的通訊,我將MicroAppStore引入了 microfe。 它具有與Redux庫類似的功能,區別在於:它對非同步資料結構更改和reducer 宣告更靈活。
伺服器端部分在實現上可能稍微複雜一些,但結構更簡單。 它只包含兩個主要部分 StitchingServer 和許多MicroAppServer。
MicroAppServer
MicroAppServer 的最小功能可以概括為 init 和 serve。
雖然 MicroAppServer 首先啟動它應該做的是使用 微應用宣告 呼叫 SticthingServer 註冊端點,該宣告定義了 MicroAppServer 的微應用 依賴關係, 型別 和 URL架構。 我認為沒有必要提及服務功能,因為沒有什麼特別之處。
StitchingServer
StitchingServer 為 MicroAppServers 提供註冊端點。 當 MicroAppServer 將自己註冊到 StichingServer 時,StichingServer 會記錄MicroAppServer 的宣告。
稍後,StitchingServer 使用宣告從請求的URL解析 MicroAppServers。
解析M icroAppServer 及其所有依賴項後,CSS,JS和HTML中的所有相對路徑都將以相關的 MicroAppServer 公共URL為字首。 另外一步是為CSS選擇器新增一個唯一的 MicroAppServer 識別符號,以防止客戶端的微應用之間發生衝突。
然後 StitchingServer 的主要職責就是:從所有收集的部分組成並返回一個無縫的HTML頁面。
其他實現一覽
甚至在2016年被稱為微前端之前,許多大公司都試圖通過 BigPipe 來解決Facebook等類似問題。 如今這個想法正在獲得驗證。 不同規模的公司對該主題感興趣並投入時間和金錢。 例如,Zalando開源了其名為Project Mosaic的解決方案。 我可以說,微型和 Project Mosaic.遵循類似的方法,但有一些重要的區別。 雖然microfe採用完全分散的路由定義來增強每個微應用的獨立性,但Project Mosaic更喜歡每條路徑的集中路由定義和佈局定義。 通過這種方式,Project Mosaic可以實現輕鬆的A/B測試和動態佈局生成。
對於該主題還有一些其他方法,例如使用iframe作為拼接層,這顯然不是在伺服器端而是在客戶端。 這是一個非常簡單的解決方案,不需要太多的伺服器結構和DevOps參與。 這項工作只能由前端團隊完成,因此可以減輕公司的組織負擔,同時降低成本。
已經有一個框架叫做 single-spa。 該專案依賴於每個應用的命名約定來解析和載入微應用。 容易掌握想法並遵循模式。 因此,在您自己的本地環境中嘗試該想法可能是一個很好的初步介紹。 但是專案的缺點是你必須以特定的方式構建每個微應用,以便他們可以很好地使用框架。
最後的想法
我相信微前端話題會更頻繁地討論。 如果該主題能夠引起越來越多公司的關注,它將成為大型團隊的事實發展方式。 在不久的將來,任何前端開發人員都可以在這個架構上掌握一些見解和經驗,這真的很有用。
考慮貢獻
我正在大力嘗試微前端,在我的腦海中有一個崇高的目標:建立一個微框架框架,可以解決大多數問題,而不會影響效能,易於開發和可測試性。 如果您有任何明智的想法,請不要猶豫,訪問我的repo,開啟問題或通過下面的評論或 Twitter DM 與我聯絡。 我會在那裡幫助你!?
翻譯:Vincent.W
原文:Understanding Micro Frontends