[book] 決戰!微前端架構(Micro Frontends in Action)
Micro Frontends in Action by Michael Geers @ Book / GitHub
微前端的概念
微前端不是一個實際技術,而是一種新的 組織及架構方法 — Micro Frontends in Action。
微前端的重要原則
- 每一個團隊都應該使用描述性的隊名,能夠清楚描述這個團隊對使用者的用途,例如,促銷團隊、結帳團隊、註冊團隊
- 各團隊能獨立進行開發、測試和部署
- 獨立開發:各個微前端團隊間要盡可能不要產生耦合;把注意力放在真正會對使用者造成衝擊的瓶頸上,而不是一昧的砍掉重複的程式碼
- 獨立部署:微前端團隊應該要能在不須與其他團隊協調的情況下更新資源檔案(assets)
- 團隊間的契約:微前端個體的開發團隊必須要能夠確定,其他團隊能正確引用他們的檔案
- 追求高效能的架構設計是打從一開始就列為高優先,而不是事後才想到的問題。
問問自己,模組和模組之間要隔離到什麼程度才能算是符合微前端 的原則?能夠獨立升級套用不同的框架版本?或者問問?為什麼不要建立一個大的 React App,由不同團隊維護同一專案中的不同塊程式碼就好?
前端整合
前端整合的三大面向:
-
路由與轉頁:把不同微前端的專案連結在同一個網站下
- 硬導覽:使用超連接
- 軟導覽:使用 App Shell
-
區塊組合:把不同微前端的專案拼接在一個頁面中
-
伺服器端整合
- Nginx SSI
- 在 Server App(例如,express)中做整合
-
客戶端整合:Web Component
-
iframe:提供最佳的技術分離,但運用大量的 iframe 會相當消耗資源
-
Ajax
-
-
區塊溝通
- 使用 query string:用在跨頁面間的資料傳遞
- 使用 attributes(類似 props):用在主要頁面傳入區塊元件
- 使用 custom event:用在區塊元件傳入主要頁面
- 使用 broadcast channel API:用在區塊元件傳到區塊元件
- 使用 window.postMessage:用在 iFrame
共用模組/共同議題
共用模組、共同議題應該要整個前端團隊一起討論,例如網頁效能、多語系、Design System、知識分享等等。
基礎設施所有權
即使採用微前端的架構,仍然會有共同的基礎架構,例如前端代理伺服器、錯誤報告、npm 套件管理、監控、日誌等。本書的作者並不建議有獨立「平台團隊(platform team)」來獨立處理這些共同的基礎架構,而是把這些功能分派給不同的前端團隊來維護。
本書作者並沒有說明為什麼他不建議有獨立的平台團隊,但我猜想可能是因為「獨立的平台團隊」將不再是使用者。開發者不是使用者的情況下,有可能做出來的東西並非使用者想要的,相反地,如果開發者本身就是使用者,那麼將更有可能設計出使用者真正需要的功能。
缺點
- 微前端架構其實一種冗餘系統,之所以採取這種架構,是因為我們評估後認為冗餘系統產生的代價讓小於互相依賴帶來的負面影響(利大於弊)
- 不同團隊間可能有更多 duplicated 的程式碼
- 用戶端可能會請求重複的 CSS、JavaScript 以及發送重複的 API
雖然我們可以讓不同專案共用樣式檔或通用的方法,但也會帶來相當多的耦合。 然而微前端的精神就在於去除耦合,並維持團隊自主性, 因此必須謹慎思考檔案是否應該被共用? 還是最好 REPEAT?
整合方式
超連結進行整合
- 作法:直接使用超連結的方式,連結到兩個獨立不同的網站
- 優點: 低耦合、 高穩健度
- 缺點: 不同頁面網址可能不同; 只有一個超連結使用者,可能難以察覺
<!-- 01_pages_links/team-decide/product/eicher.html -->
<html>
<head>
<title>Eicher Diesel 215/16</title>
<link href="/static/page.css" rel="stylesheet" />
<link href="/static/outlines.css" rel="stylesheet" />
</head>
<body class="layout">
<h1 class="header">The Tractor Store</h1>
<div class="product">
<h2>Eicher Diesel 215/16</h2>
<img class="image" src="<https://mi-fr.org/img/eicher.svg>" />
</div>
<aside class="recos">
<a href="<http://localhost:3002/recommendations/eicher>"> Show Recommendations </a>
</aside>
</body>
</html>
透過 iframe 來組合頁面
- 作法:透過 iframe 將另一個專案的內容鑲嵌近來
- 優點:
- 低耦合、 高穩健度
- 不同專案間的程式和樣式不會互相干擾
- 缺點:
- 沒有可靠的方式自動調整 iframe 的高度,Parent 需要知道並定義 iframe 的高度,不然 iframe 可能會出現 scrollbar 或空白(這會再次帶來團隊間的耦合)
- 如果頁面中有類似 Popover、Dropdown、Tooltip 這類的元件,有可能會被遮蓋而無法正確顯示
- 每個 iframe 都會佔用 CPU 和記憶體,在同一個頁面使用多個 iframe 會拖累效能
- 不利於無障礙網頁設計
- 如果有使用第三方服務追蹤使用者行為或操作的話,iframe 當中的內容可能會無法正確被讀取
- 不利於 SEO
- 備註:
- 適合靜態版面,如果是 RWD,因為 iframe 的高度會隨著使用者裝置的大小而改變
- iframe 較適合作為內部平台使用的方案,因為這多半不需要考慮太多的效能、SEO、無障礙等。
<html>
<head>
<title>Eicher Diesel 215/16</title>
<link href="/static/page.css" rel="stylesheet" />
<link href="/static/outlines.css" rel="stylesheet" />
</head>
<body class="layout">
<h1 class="header">The Tractor Store</h1>
<div class="product">
<h2>Eicher Diesel 215/16</h2>
<img class="image" src="<https://mi-fr.org/img/eicher.svg>" />
</div>
<aside class="recos">
<iframe src="<http://localhost:3002/recommendations/eicher>" />
</aside>
</body>
</html>
使用 AJAX
載入 HTML
透過 fetch 的方式載入另一個專案的 html 載入進來,方法是用 innerHTML 注入原本的頁面中
避免 CSS 樣式衝突
因為所有的 DOM 最終都呈現在同一份 HTML 頁面用,因此要避免不同專案間的 CSS 名稱互相衝突。解決的方式可以在 class name 都固定加上專案名稱作為前綴(命名空間),或者也可以使用 shadow DOM(Web Component)的方式。