在微前端中,跨應用溝通是一個非常麻煩的事,你沒辦法像平時習慣的一個 “import” 語法來傳遞共用。那到底在微前端怎麼做跨應用溝通呢?
後端有 API,那前端呢?其實大部分我們可以認知的是走網路的 HTTP 協定 API,但其實廣義來說,API 這詞彙可以用在很多地方。微服務中,微前端仰賴 HTTP 協定建構 API 的溝通橋樑,那微前端呢?
要能作為良好的 API,穩定性很重要。那擁有最穩定的 API,長期也不容易發生異動,跨專案與環境都可以很好的支援的就是原生 API,不管是走網路協定還是 Browser API 都是很好的選擇。然而要很謹慎的地方是不要依賴「狀態」來溝通,因為會有同步的問題,最理想是依靠著「傳遞」來達到溝通的目的。
其實我們可以知道,所有微前端其實都是運行在同一個 global 之下,所以只要在一樣的 global 就一定可以被溝通。
那這個 global 是誰?凡是在這個 website 下,singleton 的所有對象都可以是溝通媒介。舉凡,同一個 backend service 、同一個 window object、被定義為 singleton 的 object 都符合這個條件。
那有沒有具體方法呢?
其實我可以舉幾個常見的手法來說。
利用 module federation 共用同一包狀態管理器,不需要維護複雜的機,由它的內部機制來溝通。或是直接使用套件再共享也行,但要注意版本必須相同,否則記憶體就無法共用。
// A 應用,共享模組 store
const state = {};
const getState = () => {
return state;
};
const setState = (property, value) => {
state[property] = value;
};
export { getState, setState };
// B 應用
import { getState } from "app-a/store";
const state = getState();
// C 應用
import { setState } from "app-a/store";
setState("test", "value");
如果真的卡到版本問題,最理想還是要避免綁定在版本上,以解耦的機制來溝通最為保險。
使用 browser 機制溝通。通過 EventTarget 來做事件派發,可以向 window, document … 等等物件來進行溝通。或是使用 broadcast channel, message event, service worker 等等方式觸發。搭配 localStorage, sessionStorage, cookie, … 等等方式進行資料傳遞。
// event-bus.js
const eventBus = {
on: (type, callback) => {
window.addEventListener(type, callback);
},
off: (type, callback) => {
window.removeEventListener(type, callback);
},
emit: (type, detail) => {
window.dispatchEvent(new CustomEvent(type, { detail }));
},
};
基本上所有微前端建構的網頁依然在同一個 Document 下,所以依然能夠仰賴 DOM 的物件實體來達到溝通,甚至搭配冒泡機制取得事件的資訊。所以也可以用事件冒泡來取得事件觸發。
const emit = (dom, type, detail) => {
dom.dispatchEvent(
new CustomEvent(type, {
detail,
bubbles: true,
cancelable: true,
composed: true,
})
);
};
利用瀏覽器原生的 WebComponent 來溝通,它可以傳遞 attribute
, event
, slot template
等多種資訊,具備懶加載特性,可以說是非常彈性的解決方案。
class CustomComponent extends HTMLElement {
// impl...
}
customElements.define("custom-component", CustomComponent);
關於更詳細的實作就不在這贅述,後面別的章節再詳細說明使用。
使用後端串接 WebSocket 來溝通,以後端作為溝通橋樑,將資料存在後端,並使用 WebSocket 進行事件觸發。
可以應用 HTTP Request + WebSocket,一樣採用網路手段來達到溝通,API 設計只需要可考慮 HTTP 手段,不需要額外學習溝通的手段。但問題在會消費一定的網路資源,而微前端只是在前端溝通,卻要追加這層流量的消費。
在 ServiceWork 可以去攔截對外部發出的 HTTP 請求,就可以假裝提供了某個服務,能不向網路溝通就完成內部服務的自給自足。也可以作為「假裝是 Backend API」來提供服務,學習成本和直覺度都非常高,就像是真的在實作微服務一樣。
以上我只是寫出一些常見有討論度的方法,以及自己在 POC 玩過的一些手段,其實微前端的溝通方式發揮一點創意還是能夠有很多作法。如果有其他你知道的方式,歡迎來跟我分享喔~