微前端到底是什麼?提前瞭解避免採坑

雲棲號資訊:【點擊查看更多行業資訊
在這裏您可以找到不同行業的第一手的上雲資訊,還在等什麼,快來!


一、簡介

爲了解決龐大的一整塊後端服務帶來的變更與擴展方面的限制,出現了微服務架構(Microservices):

微服務是面向服務架構(SOA)的一種變體,把應用程序設計成一系列松耦合的細粒度服務,並通過輕量級的通信協議組織起來。

具體地,將應用構建成一組小型服務。這些服務都能夠獨立部署、獨立擴展,每個服務都具有穩固的模塊邊界,甚至允許使用不同的編程語言來編寫不同服務,也可以由不同的團隊來管理。

然而,越來越重的前端工程也面臨同樣的問題,自然地想到了將微服務思想應用(照搬)到前端,於是有了微前端(micro-frontends)的概念:

Micro frontends, An architectural style where independently deliverable frontend applications are composed into a greater whole.

即,一種由獨立交付的多個前端應用組成整體的架構風格。具體的,將前端應用分解成一些更小、更簡單的能夠獨立開發、測試、部署的小塊,而在用戶看來仍然是內聚的單個產品:

Decomposing frontend monoliths into smaller, simpler chunks that can be developed, tested and deployed independently, while still appearing to customers as a single cohesive product.

二、特點

簡單來講,微前端的理念類似於微服務:

In short, micro frontends are all about slicing up big and scary things into smaller, more manageable pieces, and then being explicit about the dependencies between them.

將龐大的整體拆成可控的小塊,並明確它們之間的依賴關係。關鍵優勢在於:

  • 代碼庫更小,更內聚、可維護性更高;
  • 松耦合、自治的團隊可擴展性更好;
  • 漸進地升級、更新甚至重寫部分前端功能成爲了可能;

簡單、松耦合的代碼庫

  • 比起一整塊的前端代碼庫,微前端架構下的代碼庫傾向於更小/簡單、更容易開發;
  • 此外,更重要的是避免模塊間不合理的隱式耦合造成的複雜度上升。通過界定清晰的應用邊界來降低意外耦合的可能性,增加子應用間邏輯耦合的成本,促使開發者明確數據和事件在應用程序中的流向

增量升級

理想的代碼自然是模塊清晰、依賴明確、易於擴展、便於維護的……然而,實踐中出於各式各樣的原因:

  • 歷史項目,祖傳代碼
  • 交付壓力,當時求快
  • 就近就熟,當時求穩……

總存在一些不那麼理想的代碼:

  • 技術棧落後,甚至強行混用多種技術棧;
  • 耦合混亂,不敢動,牽一髮何止動全身;
  • 重構不徹底,重構-爛尾,換個姿勢重構-又爛尾……

而要對這些代碼進行徹底重構的話,最大的問題是很難有充裕的資源去大刀闊斧地一步到位,在逐步重構的同時,既要確保中間版本能夠平滑過渡,同時還要持續交付新特性:

In order to avoid the perils of a full rewrite, we’d much prefer to strangle the old application piece by piece, and in the meantime continue to deliver new features to our customers without being weighed down by the monolith.

所以,爲了實施漸進式重構,我們需要一種增量升級的能力,先讓新舊代碼和諧共存,再逐步轉化舊代碼,直到整個重構完成。

這種增量升級的能力意味着我們能夠對產品功能進行低風險的局部替換,包括升級依賴項、更替架構、UI 改版等。另一方面,也帶來了技術選型上的靈活性,有助於新技術、新交互模式的實驗性試錯。

獨立部署

獨立部署的能力在微前端體系中至關重要,能夠縮小變更範圍,進而降低相關風險。

因此,每個微前端都應具備有自己的持續交付流水線(包括構建、測試並部署到生產環境),並且要能獨立部署,不必過多考慮其它代碼庫和交付流水線的當前狀態:

1

就算舊的系統是按固定週期季度發佈或手動發佈的,甚至隔壁團隊誤發佈了一個半成品或有問題的特性也無關緊要。也就是說,如果一個微前端已經準備好發佈了,它就應該隨時可發佈,並且只由開發維護它的團隊來定。
P.S.甚至還可以結合BFF 模式實現更進一步的獨立:

2

團隊自治

除代碼庫及發佈週期上的解耦之外,微前端還有助於形成完全獨立的團隊,由不同團隊各自負責一塊產品功能從構思到發佈的整個過程,團隊能夠完全擁有爲客戶提供價值所需的一切,從而快速高效地運轉。

爲此,應該圍繞業務功能縱向組建團隊,而不是基於技術職能劃分。最簡單的,可以根據最終用戶所能看到的內容來劃分,比如將應用中的每個頁面作爲一個微前端,並交給一個團隊全權負責。與基於技術職能或橫向關注點(如樣式、表單、校驗等)組織的團隊相比,這種方式能夠提升團隊工作的凝聚力。

3

三、實現方案

實現上,關鍵問題在於:

  • 多個 Bundle 如何集成?
  • 子應用之間怎樣隔離影響?
  • 公共資源如何複用?
  • 子應用間怎樣通信?
  • 如何測試?

多 Bundle 集成

微前端架構中一般會有個容器應用(container application)將各子應用集成起來,職責如下:

  • 渲染公共的頁面元素,比如 header、footer;
  • 解決橫切關注點(cross-cutting concerns),如身份驗證和導航;
  • 將各個微前端整合到一個頁面上,並控制微前端的渲染區域和時機;

集成方式分爲 3 類:

  • 服務端集成:如 SSR 拼裝模板;
  • 構建時集成:如 Code Splitting;
  • 運行時集成:如通過 iframe、JS、Web Components 等方式;

服務端集成

服務端集成的關鍵在於如何保證各部分模板(各個微前端)能夠獨立發佈,必要的話,甚至可以在服務端也建立一套與前端相對應的結構:

6

構建時集成

常見的構建時集成方式是將子應用發佈成獨立的 npm 包,共同作爲主應用的依賴項,構建生成一個供部署的 JS Bundle。
然而,構建時集成最大的問題是會在發佈階段造成耦合,任何一個子應用有變更,都要整個重新編譯,意味着對於產品局部的小改動也要發佈一個新版本,因此,不推薦這種方式。

運行時集成

將集成時機從構建時推遲到運行時,就能避免發佈階段的耦合。常見的運行時集成方式有:

  • iframe;
  • JS:比如前端路由;
  • Web Components;

雖然直覺上用 iframe 好像不太好(性能、通信成本等),但在這裏確實是個合理選項,因爲 iframe 無疑是最簡單的方式,還天然支持樣式隔離以及全局變量隔離,但這種原生的隔離性,意味着很難把應用的各個部分聯繫到一起,路由控制、歷史棧管理、深度鏈接(deep-linking)、響應式佈局等都變得異常複雜,因而限制了 iframe 方案的靈活性。

另一種最常見的方式是前端路由,每個子應用暴露出渲染函數,主應用在啓動時加載各個子應用的獨立 Bundle,之後根據路由規則渲染相應的子應用。目前看來,是最靈活的方式。

還有一種類似的方式是Web Components,將每個子應用封裝成自定義 HTML 元素(而不是前端路由方案中的渲染函數),以獲得Shadow DOM帶來的樣式隔離等好處。

影響隔離

子應用之間,以及子應用與主應用間的樣式、作用域隔離是必須要考慮的問題,常見解決方案如下:

  • 樣式隔離:開發規範(如BEM)、CSS 預處理(如SASS)、模塊定義(如CSS Module)、用 JS 來寫(CSS-in-JS)、以及shadow DOM特性;
  • 作用域隔離:各種模塊定義(如ES Module、AMD、Common Module、UMD);

資源複用

資源複用對於 UI 一致性和代碼複用有重要意義,但並非所有的可複用資源(如組件)都必須在一開始就提出來複用,建議的做法是前期允許一定程度的冗餘,各個 Bundle 在各自的代碼庫中創建組件,直到形成相對明確的組件 API 時再建立可供複用的公共組件。

另一方面,資源分爲以下 3 類:

  • 基礎資源:完全不含邏輯功能的圖標、標籤、按鈕等;
  • UI 組件:含有一定 UI 邏輯的搜索框(如自動完成)、表格(如排序、篩選、分頁)等;
  • 業務組件:含有業務邏輯;

其中,不建議跨子應用複用業務組件,因爲會造成高度耦合,增加變更成本。

對於公共資源的歸屬和管理,一般有兩種模式:

公共資源歸屬於所有人,即沒有明確歸屬;
公共資源歸集中管理,由專人負責;

從實踐經驗來看,前者很容易衍變成沒有明確規範,且背離技術願景的大雜燴,而後者會造成資源創建和使用的脫節,比較推薦的模式是開源軟件的管理模式:

Anyone can contribute to the library, but there is a custodian (a person or a team) who is responsible for ensuring the quality, consistency, and validity of those contributions.

即,所有人都能補充公共資源,但要有人(或一個團隊)負責監管,以保證質量、一致性以及正確性。

應用通信

通過自定義事件間接通信是一種避免直接耦合的常用方式,此外,React 的單向數據流模型也能讓依賴關係更加明確,對應到微前端中,從容器應用向子應用傳遞數據與回調函數。

另外,路由參數除了能用於分享、書籤等場景外,也可以作爲一種通信手段,並且具有諸多優勢:

  • 其結構遵從定義明確的開放標準;
  • 頁面級共享,能夠全局訪問;
  • 長度限制促使只傳遞必要的少量數據;
  • 面向用戶的,有助於依照領域建模;
  • 聲明式的,語義上更通用(”this is where we are”, rather than “please do this thing”);
  • 迫使子應用之間間接通信,而不直接依賴對方;

但原則上,無論採用哪種方式,都應該儘可能減少子應用間的通信,以避免大量弱依賴造成的強耦合。

測試

每個子應用都應該有自己的全套測試方案,特殊之處在於,除單元測試、功能測試外,還要有集成測試:

  • 集成測試:保證子應用間集成的正確性,比如跨子應用的交互操作;
  • 功能測試:保證頁面組裝的正確性;
  • 單元測試:保證底層業務邏輯和渲染邏輯的正確性;

自下而上形成一個金字塔結構,每一層只需驗證在其下層覆蓋不到的部分即可。

四、缺點

當然,這種架構模式並非百益而無一害,一些問題也隨之而來:

  • 導致依賴項冗餘,增加用戶的流量負擔;
  • 團隊自治程度的增加,可能會破壞協作;

流量負擔

獨立構建意味着公共資源的冗餘,繼而增加用戶的流量負擔。
沒有非常理想的解決辦法,一種簡單的方案是將公共依賴從(子應用的)構建產物中剔除,但又會引入構建時耦合:

Now there is an implicit contract between them which says, “we all must use these exact versions of these dependencies”.

操作/管理上的複雜性

在採用微前端之前,先要考慮幾個問題:

  • 現有的前端開發、測試、發佈流程如何擴展支持很多個應用?
  • 分散的,控制弱化的工具體系及開發實踐是否可靠?
  • 針對各式各樣的前端代碼庫,如何建立質量標準?

總之,與之前不同的是,微前端將產生一堆小的東西,因此需要考慮是否具備採用這種方法所需的技術和組織成熟度。

五、總結

類似於微服務之於後端,前端業務在發展到一定規模之後,也需要一種用來分解複雜度的架構模式,於是出現了微服務思想在前端領域的應用,即微前端。主要目的在於:

  • 技術架構上進一步的擴展性(模塊邊界清晰、依賴明確);
  • 團隊組織上的自治權;
  • 開發流程上能獨立開發、獨立交付;

最大的意義在於解鎖了多技術棧並存的能力,尤其適用於漸進式重構中架構升級過渡期:

Suddenly we are not tightly coupled with one stack only, we can refactor legacy projects supporting the previous stack and a new one that slowly but steadily kicks into production environment without the need of a big bang releases (see strangler pattern).

允許低成本嘗試新技術棧,甚至允許選用最合適的技術棧做不同的事情(類似於微服務中允許用不同的語言編寫不同服務):

we can use different version of the same library or framework in production without affecting the entire application, we can try new frameworks or approaches seeing real performances in action, we can hire the best people from multiple communities and many other advantages.

【雲棲號在線課堂】每天都有產品技術專家分享!
課程地址:https://yqh.aliyun.com/live

立即加入社羣,與專家面對面,及時瞭解課程最新動態!
【雲棲號在線課堂 社羣】https://c.tb.cn/F3.Z8gvnK

原文發佈時間:2020-07-21
本文作者:hh_phoebe
本文來自:“掘金”,瞭解相關信息可以關注“掘金”

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章