Chuyển đến nội dung chính

第 14 課:跨 MFE 狀態管理與通信

微前端的狀態管理模式。自訂事件、事件匯流排、基於 URL 的狀態、瀏覽器儲存。避免共享全域狀態。 React Context、CustomEvent 的實用模式。

🏗️ 建築 — 第 14 課 第 14 課:跨 MFE 狀態管理和 通訊

微服務與微前端系統設計-從基礎到生產

第 5 部分:建構實用的微前端

亞洲開發網

簡介

跨MFE通訊是微前端最大的挑戰。每個 MFE 都在自己的運行時中運行,沒有共享記憶體。本文介紹安全通訊模式。


1. 黃金法則

MFE 應該是松耦合——透過合約進行通信,而不是共享狀態。

❌ Shared Global State:
window.globalStore = { cart: { items: [...] } }
→ Coupling nightmare, race conditions

✅ Event-based Communication:
MFE A publishes: "cart:item-added" { productId, qty }
MFE B subscribes: update cart badge
→ Loosely coupled, observable

2. 溝通模式

2.1 自訂事件(瀏覽器本機)

// Product MFE - Publish
window.dispatchEvent(new CustomEvent('cart:item-added', {
  detail: { productId: '123', name: 'Laptop', price: 999 }
}));

// Cart MFE - Subscribe
useEffect(() => {
  const handler = (e) => {
    setCartItems(prev => [...prev, e.detail]);
  };
  window.addEventListener('cart:item-added', handler);
  return () => window.removeEventListener('cart:item-added', handler);
}, []);

2.2 事件匯流排(自訂)

class EventBus {
  private handlers: Map<string, Set<Function>> = new Map();
  
  on<T>(event: string, handler: (payload: T) => void) {
    if (!this.handlers.has(event)) this.handlers.set(event, new Set());
    this.handlers.get(event)!.add(handler);
    return () => this.handlers.get(event)?.delete(handler);
  }
  
  emit<T>(event: string, payload: T) {
    this.handlers.get(event)?.forEach(h => h(payload));
  }
}

export const eventBus = new EventBus();

2.3 基於 URL 的狀態

/products?category=electronics&sort=price
→ URL là "shared state" tự nhiên: bookmarkable, shareable, no coupling

2.4 瀏覽器存儲

// Shared preferences via localStorage
localStorage.setItem('user:preferences', JSON.stringify({
  currency: 'VND', language: 'vi', theme: 'dark'
}));

3. 國家所有權規則

狀態業主MFE 如何存取
授權狀態殼牌背景/事件
購物車商品購物車 MFE活動
產品資料產品MFE未分享
使用者偏好殼牌本地儲存 + 事件
URL 參數外殼(路由器)網址
使用者介面主題殼牌CSS 變數

4. 契約優先的通信

// shared-types/events.ts
interface CartItemAddedEvent {
  type: 'cart:item-added';
  payload: { productId: string; name: string; price: number; quantity: number; };
}

interface CartUpdatedEvent {
  type: 'cart:updated';
  payload: { itemCount: number; total: number; };
}

5. 最佳實踐

Mỗi MFE nên:
✅ Fetch dữ liệu riêng (own API calls)
✅ Manage state riêng (local state)
✅ Render independently
✅ Graceful degradation

Mỗi MFE KHÔNG nên:
❌ Import components từ MFE khác
❌ Access DOM của MFE khác
❌ Share runtime state qua window/global
❌ Assume MFE khác đã loaded

總結

  • 自訂事件 — 簡單,瀏覽器原生
  • 事件匯流排 — 類型化、可測試
  • URL — 最適合導航狀態
  • localStorage — 使用者首選項
  • 規則:每個 MFE 管理自己的狀態,透過事件進行通信

下一篇文章: 第 15 課:設計系統和共享 UI — 保持 UX 一致