後端有 Microservices,那前端呢?初探 Micro Frontends 的世界
因此 Web Components 的出現就是希望可以解決上述的問題,而 Web Components 一共由以下三種元素組成:
- Custom elements
自訂一個語意化標籤來引用 components,並且利用 JavaScript 來建立 custom elements、shadow DOM、HTML templates 三者之間的關聯,這種自定義標籤會像這樣: <my-custom-element></my-custom-element>
。
謎之音:這根本就是 Angular 的寫法阿XD
- Shadow DOM
Shadow DOM 簡單來說就是在現有的 DOM tree 中產生出一個完全獨立於其他元素的 sub DOM tree,藉由此方法就可以讓 sub DOM tree 自己獨立運作並且不會干擾到其他 DOM tree 上的元素。
- HTML templates
利用 <template>
以及 <slot>
元素產生出具有複用性的 HTML 樣板。
感覺上 Web Components 的設計準則就可以當作是 Micro Frontends 的最好的實現方式,透過上述的三種方式就可以產生出一個獨立於原本專案而且又不會影響到頁面的 modules。
可是這種開發方式其實不太符合每一種框架的設計理念(可能只有 Angular 體系最適合XD),而且會讓一個 reusable component 開發起來蠻冗長的,所以 Micro Frontends 還是沒有被很多前端工程師所重視。
- Webpack Module Federation
假設我們今天有 A、B 兩個專案,其中 A 專案有元件發現是可以讓 B 專案進行複用的,這時候我們會有以下幾種作法達到這個需求:
- 將 A 專案中可以被複用的元件手動複製其程式碼至 B 專案。
- 將 A 專案中可以被複用的元件發佈到 npm 上,並且在 B 專案中安裝。
- 融合 A 專案與 B 專案並用 monorepo 的架構進行開發。
相信不管是哪個方法都相當複雜而且後續的維護成本也很高,而且隨著專案越來越大,之後的網站效能也是問題,即便用了 code splitting 或者是 dynamic import 能提昇的效能也有限。
不過這個聲音 Webpack 聽到了,在 Webpack 5 誕生後推出了一個全新的技術叫 Module Federation,Module Federation 的誕生就是為了幫助開發者可以 import 外部專案已經 bundle 好的檔案,不用再經由額外的安裝步驟相當方便。
Micro Frontends 實作 ft. Webpack Module Federation
上面介紹完 Micro Frontends 的實現方式後,接下來就要進入本篇文章的實作篇了,這邊筆者會以 Webpack Module Federation 進行 Demo。
首先筆者先介紹 Webpack Module Federation 的相關設定,在 Webpack Module Federation 的世界中我們一共會分為 Remote 以及 Host 兩種設定區塊,分別代表的是 modules export 以及 import 的部分,首先先介紹 Remote 的部分,在這個 plugin 中我們一共可以設定幾個內容:
- name
設定 export 出去的名字。
- filename
設定 export 出去的檔案名稱,最終的使用方式會在下方的 import 區塊做介紹,官方建議使用 remoteEntry.js
作為命名。
- exposes
exposes 就是負責設定 modules 中需要被 export 的 components 檔案 path alias,通常這邊的寫法都會是 './componentName': 'componentPath'
,在 key 的部分會用相對路徑的寫法原因是未來在 import 的時候就可以用上方設定好的 name 作為檔案抓去的進入點。
舉例來說:我們有一個 Button.jsx
的檔案,這時候我們就可以在 exposes 的區塊寫上 './Button': './src/Button'
,到時候在 import 的時候就可以寫上 'name/Button
的方式抓去這個檔案了。
- shared
shared 則是當 components 有使用到相同的第三方套件時,可以用來設定的相關規範,基本上 shared 一共有三種呈現方式:
- Array syntax:只要在 Array 內寫上 package name 即可,寫法上會像這樣:
shared: ['lodash']
- Object syntax:可以更詳細的設定 package,會以 package name 作為 Object key,後方的 value 則可以設定此 package 的版本,寫法上會像這樣:
shared: { lodash: '^4.17.0' }
- Object with sharing hints:基於 Object syntax 的架構上,填入 Webpack Module Federation 可支援的相關 Sharing hints。
在 Object with sharing hints 這邊一共可以設定非常多種內容,筆者這裡只單純說明幾個最常用的 sharing hints,想要了解所有的 sharing hints 的讀者可以參考這個連結。
- eager:讓 Webpack 可以直接存取 modules,如果設定為 false 則使用 async fetch 的方式去取得分離後的 chunk,若設定為 true 則會打包到 remoteEntry 方便進行直接抓取。
- requiredVersion:設定此 package 的版本。
- singleton:使用 singleton 就代表此 package 只允許一個版本,並且只加載一次,通常在
react
、react-dom
這種有 global internal state 的 libarary 加上此設定更是重要。
在 Shared 內筆者建議的寫法是基於目前專案的 package dependencies 上再額外疊加一些自己想要設定的 Object with sharing hints,最終設定完的長像會像下圖這樣:
這裡筆者提醒一下讀者,假如讀者在設定的過程中有遇到這個錯誤:Uncaught Error: Shared module is not available for eager consumption
代表讀者需要額外在新增一個檔案將 component render 到 DOM 的相關程式碼都複製上去,最後再到 index.jsx
中去 import 這個檔案。
最終呈現在網頁上的畫面就會像下圖這樣:
再來是 Host 的部分,基本上設定的內容跟 Remote 大同小異,差別在於 Remote 需要設定 exposes 的 components 以及 filename ,而 Host 則是要設定該 Remote 的檔案位置。
在檔案位置中我們會以三種內容組合,寫法上會像這樣: name@remoteURL/filename
,其中 name 就是上方介紹到的 export 出去的名稱,remoteURL 則是 Remote 區塊內 Webpack config 中所設定的 output path 相關設定,而 filename 則是上方介紹到的 export 出去的檔案名稱。
最終的寫法就會像下方這樣:
由於筆者在 shared 的區塊都沒有加上 eager: true
,所以這邊在抓取 components 時都是用 async 的方式去抓取,因此筆者建議會使用 <Suspense>
的方式進行 data fetching 這樣會比較安全喔!
最終呈現在網頁上的畫面就會像下圖這樣:
最後再附上這次的 demo repo 連結,有興趣的讀者歡迎下載來玩玩看喔!
小結
這次介紹了 Micro Frontends 以及 Microservices 這兩種目前最多人討論的架構,希望可以讓讀者更了解一些新時代的架構所帶來的優缺點,文章的篇幅可能有點多,讀者可以邊看邊消化一下這樣才能更好的理解所有的內容。
最後也稍微介紹了一下 Micro Frontends 的實作,內容有點多可能比較難消化,如果讀者有任何文章上的問題都歡迎留言給我,至於 Microservices 的實作方式,由於筆者比較熟悉前端就無法介紹給讀者了?
最後如果讀者對於文章有任何問題都歡迎留言給我,之後筆者也會盡量多學一些不同的架構以及相關的知識再分享給大家,感謝各位讀者的收看~
瀏覽 1,703 次