Module Federation 加入 Rspack

2024 年 1 月 9 日

Rspack 0.5.0 版本發佈

最新的 Rspack 0.5.0 版本引入了備受期待的 Module Federation 以及新的 "v1.5" federation API。這標誌著 federation 自推出以來最大幅度的改版。v1.5 為終端使用者和框架作者提供了額外的功能,這是原始設計無法實現的壯舉。

Rspack with Infinity Gauntlet

Webpack Federation 獲得了一些關愛!

Federation API 已開放給使用者進行豐富、擴展或管理生命週期。雖然 v1.5 帶有幾個新功能,但它並未對原始 Module Federation 的 API 引入重大變更。

v1.5 也可透過 @module-federation/enhanced 在 webpack 中使用,並且上層外掛生態系統,例如 next.js federation 或 node.js federation 外掛,已在其 Canary 版本中利用 v1.5。

在 Rspack 中,Module Federation v1.5 可以透過 rspack.container.ModuleFederationPlugin 使用,而原始的 Module Federation 可以透過 rspack.container.ModuleFederationPluginV1 使用。

遷移機會

Rspack 中對 Module Federation 的支援開啟了多種創新的遷移選項,透過在執行時共享程式碼來加速捆綁工具。Webpack 和 Rspack 都可以共享程式碼,依賴 Module Federation Group 在 v1.5 中引入的相同集中式執行時。這確保了保持功能對等是可管理的,並且不需要額外的 Module Federation 分支來進行自訂。

透過 federation 逐步遷移到 Rspack 可以實現。如果您有被鎖定的 webpack 外掛或無法完全切換到 rspack,透過 module federation,您可以讓 Rspack 和 webpack 共享依賴項和程式碼,這意味著更多的程式碼可以透過 Rspack 建置,而 webpack 主機的工作量更少,但仍然獲得相同的結果。範例:Webpack Rspack 互通

透過 federation 共享 node_modules 來加速建置。可以告訴 webpack import: false 它們,而 Rspack 可以編譯所有共享模組,減少解析開銷和 webpack 部分必須執行的程式碼量,方法是將其委派給 Rspack,在 Rspack 中,類似的工作負載只需幾毫秒即可執行。範例:將 Rspack Vendor 卸載到 Webpack 應用程式

一次遷移一個。由於 webpack (@module-federation/enhanced) 和 Rspack 之間的介面是共享的,因此使用者可以將任何現有的 federation 建置或遠端切換到 Rspack。我們建議任何剩餘的 webpack 建置都使用 @module-federation/enhanced,它利用我們的新設計並匯出 ModuleFederationPlugin。但是,您仍然可以使用 webpack 核心中隨附的現成外掛。Rspack 應能與現有的 federation 應用程式無縫銜接。

與 webpack federation 的速度比較

在一個簡單的比較中,使用 module federation 範例

  • 應用程式:5
  • Webpack:每次建置 500-3000 毫秒 - 生產
  • Rspack:每次建置 130-350 毫秒 - 生產

一般來說,我們觀察到 federation 應用程式的建置速度提高了 5-10 倍,大致與我們在 rspack 中看到的典型效能提升一致。大多數在 module federation 範例中的建置。我們轉換的開發建置通常需要不到 150 毫秒才能冷啟動。

Rsbuild 支援

Rsbuild 繼續提供簡化建置設定的方法。它使使用 Rspack 的感覺不像處理基於 webpack 的建置系統。雖然它與 module federation 相容,但將會利用 Rsbuild 來提供更精簡的 module federation 體驗。例如,用於 React 的 Rsbuild 外掛可以自動共享預設值,或者 Rsbuild 可以提供方便的預設和模式。

我們已經開始將一些 module federation 範例遷移到 Rspack 和 Rsbuild。一個值得注意的範例是 CRA 遷移,它很順暢,只需幾分鐘即可從 CRA 切換到 Rsbuild 在此。本指南對於尋求輕鬆提高老舊建置效能的 CRA 使用者也很有幫助:Rsbuild 遷移指南。Rsbuild 對於將 Vue 範例從 vue-cli 遷移到更快、更容易且對 federation 友善的東西也非常棒。

federation v1 和 v1.5 之間的差異

最初,Federation 非常簡單。RemoteEntry 公開了 {get, init} 介面,而沒有太多其他內容。這最終非常有限,但很簡單。隨著複雜用途的增長和更多功能的發現,我們顯然需要更多控制權,而不僅僅是最初的想法,即在建置之間共享程式碼並載入它。

v1.5 引入了 runtimePlugins。這些可以透過 runtimePlugins 選項在編譯時新增。但是您也可以在執行時動態地在 javascript 檔案中註冊它們。

在 Rspack 中

const rspack = require('@rspack/core');

new rspack.container.ModuleFederationPlugin({
  name: 'app1',
  filename: 'static/js/remoteEntry.js',
  exposes: {
    './Button': './src/components/button.js',
  },
  runtimePlugins: [require.resolve('./my-custom-plugin')]
  remotes: {
    app2: 'app2@https://127.0.0.1:3002/static/js/remoteEntry.js',
  },
  shared: {
    react: { singleton: true },
    'react-dom': { singleton: true },
  },
})

而對於 Webpack

const { ModuleFederationPlugin } = require('@module-federation/enhanced');

new ModuleFederationPlugin({
  name: 'app1',
  filename: 'static/js/remoteEntry.js',
  exposes: {
    './Button': './src/components/button.js',
  },
  runtimePlugins: [require.resolve('./my-custom-plugin')]
  remotes: {
    app2: 'app2@https://127.0.0.1:3002/static/js/remoteEntry.js',
  },
  shared: {
    react: { singleton: true },
    'react-dom': { singleton: true },
  },
})

Federation 也可以以動態方式使用,而無需編譯時外掛。您可以在此處閱讀更多關於 v1.5 執行時的資訊

// Can load modules using only the runtime SDK without relying on build plugins
// When not using build plugins, shared dependencies cannot be automatically reused
import { init, loadRemote } from '@module-federation/runtime-tools';
import customPlugin from './runtimePlugin';

init({
  name: 'app1',
  remotes: [
    {
      name: 'runtime_remote1',
      alias: 'app2',
      entry: 'https://127.0.0.1:3006/remoteEntry.js',
    },
  ],
  shared: {
    react: {
      version: '18.2.0',
      scope: 'default',
      lib: () => React,
      shareConfig: {
        singleton: true,
        requiredVersion: '>17',
      },
    },
    'react-dom': {
      version: '18.2.0',
      scope: 'default',
      lib: () => ReactDOM,
      shareConfig: {
        singleton: true,
        requiredVersion: '>17',
      },
    },
  },
  plugins: [customPlugin()],
});

// Load by alias
loadRemote <
  { add: (...args: Array<number>) => number } >
  'app2/util'.then(md => {
    md.add(1, 2, 3);
  });

閱讀更多關於 Federation 1.5 更新的資訊:Module Federation 1.5