Upgrading React with micro-frontends

Alex O'Callaghan

透過微前端升級 React

原文由 Alex O'Callaghan 發布,訂閱此部落格

2021 年,我們使用 React 17 部署了微前端架構。我們依循建議,將 reactreact-dom 設為共用的 singleton 模組,讓整個應用程式在單一的 React 樹中渲染。隨著許多熱門相依套件開始要求 React 18,加上即將到來的 React 19,我們必須在微前端中升級 React。由於涉及眾多團隊,我們必須分階段逐步進行。

引進橋接元件

2026 年更新我們後來將自行開發的微前端 React 橋接套件開源,詳情請見開源我們的微前端 React 橋接套件一文。本文介紹的是我們初期使用 @module-federation/bridge-react 套件的做法。

為了讓微前端在獨立的 React 樹中渲染,我們加入了一個「橋接」元件包裝層。這讓我們能在不影響 shell 的情況下,在微前端中升級 React。

@module-federation/bridge-react 套件提供了一些輔助工具來支援這種做法。在微前端中,我們新增了一個 export-app.tsx 檔案,將進入點的 React 元件用橋接包裝起來:

// src/export-app.tsx
import { createBridgeComponent } from "@module-federation/bridge-react";
import App from "./App";

export default createBridgeComponent({
  rootComponent: App,
});

export const BRIDGE = true;

我們也需要更新 ModuleFederationPlugin,以暴露這個新的進入點檔案。為了升級到比 shell 更新的版本,我們必須停止將 reactreact-dom 作為 singleton 共用。

// webpack.config.js
module.exports = {
  plugins: [
    new ModuleFederationPlugin({
      name: "app",
      filename: "remoteEntry.js",
      exposes: {
        "./App": "./src/export-app",
      },
      shared: ["react", "react-dom"],
    }),
  ],
};

在我們的 shell 應用程式中,我們使用 import-remote 來動態載入 remoteEntry 檔案。為了維持向下相容,我們需要調整這段邏輯,使其同時能載入直接暴露與經過橋接的 React 元件:

import { createRemoteComponent } from '@module-federation/bridge-react';

const SomeRemote = React.lazy(async () => {
  const remote = await importRemote({
    url: '/bundles/remote-app',
    scope: 'remote-app',
    module: 'App',
  });

  if (remote.BRIDGE) {
    return {
      default: createRemoteComponent({
        loader: () => remote,
        loading: <div>Loading...</div>,
        fallback: (info) =>
          <div>Failed to load {info.error.message}</div>,
      });
    }

    return remote;
  }
});

這裡有幾個重點:

  • 我們使用 importRemote 來延遲載入 remoteEntry.js 檔案。這可以避免載入不必要的微前端。
  • 我們檢查一個匯出的常數 BRIDGE。它會告訴我們該微前端是否已用橋接元件包裝。這讓各團隊可以自行選擇是否採用橋接,同時保持對尚未遷移者的相容性。
  • 我們將橋接元件放在帶有 default 屬性的物件中回傳,以模擬 React.lazy 預期接收的動態載入模組的結構。

這些變更讓我們得以將個別的微前端升級到 React 18,而無需升級 shell。一旦所有微前端都採用橋接的做法,我們就可以在不需要升級微前端的情況下升級 shell。

本文章由 muse-spark-1.2-contributor 進行翻譯

留言