Rspack 中添加了模块联邦

2024 年 1 月 9 日

介绍 Rspack 0.5.0

最新的 Rspack 0.5.0 引入了备受期待的模块联邦,以及新的 "v1.5" 联邦 API。这是自模块联邦诞生以来最重大的改造。v1.5 为最终用户和框架作者提供了额外的功能,这是原始设计无法实现的壮举。

Rspack with Infinity Gauntlet

Webpack 联邦得到了关注!

联邦 API 已向用户开放,以便用户丰富、扩展或管理生命周期。虽然 v1.5 带来了几个新功能,但它并没有对原始模块联邦的 API 引入重大更改。

v1.5 也可通过 @module-federation/enhanced 为 webpack 访问,上层插件生态系统(如 next.js 联邦或 node.js 联邦插件)已在其 canary 版本中使用 v1.5。

在 Rspack 中,模块联邦 v1.5 可以通过 rspack.container.ModuleFederationPlugin 使用,而原始模块联邦可以通过 rspack.container.ModuleFederationPluginV1 使用。

迁移机会

Rspack 中对模块联邦的支持开辟了许多创造性的迁移选项,通过在运行时共享代码来加速捆绑器工具。Webpack 和 Rspack 都可以共享代码,依赖于模块联邦组在 v1.5 中引入的相同集中式运行时。这确保了保持功能一致性是可控的,并且不需要对模块联邦进行额外的 fork 来定制它。

渐进迁移可以通过联邦实现。如果您有 webpack 锁定的插件,或者无法完全切换到 rspack,通过模块联邦,您可以允许 Rspack 和 webpack 共享依赖项和代码,这意味着更多代码可以通过 Rspack 构建,而 webpack 主机的工作量更少,但仍然可以获得相同的结果。 示例:Webpack Rspack 交互

通过联邦共享 node_modules 来加速构建。可以使用 webpack 将它们设置为 import: false,而 Rspack 可以编译所有共享模块,减少解析开销和 webpack 部分需要执行的代码量,通过将这些工作委托给 Rspack,在那里类似的工作负载只需几毫秒即可完成。 示例:将 Rspack 供应商卸载到 Webpack 应用程序

一次迁移一个。由于 webpack (@module-federation/enhanced) 和 Rspack 之间的接口是共享的,用户可以将任何现有的联邦构建或远程切换到 Rspack。我们建议使用 @module-federation/enhanced 的任何剩余 webpack 构建,该构建利用了我们的新设计并导出 ModuleFederationPlugin。但是,您仍然可以使用 webpack 核心提供的 stock 插件。Rspack 应该可以无缝地与现有的联邦应用程序集成。

与 webpack 联邦的性能比较

在一个简单的比较中,使用模块联邦 示例

  • 应用程序:5
  • Webpack:生产环境下每次构建 500-3000 毫秒
  • Rspack:生产环境下每次构建 130-350 毫秒

一般来说,我们观察到联邦应用程序的构建速度提高了 5-10 倍,与我们在 rspack 中看到的典型性能提升大体一致。大多数构建都在模块联邦示例中。我们转换的开发构建通常只需不到 150 毫秒即可进行冷启动。

Rsbuild 支持

Rsbuild 继续提供一种简化的构建配置方法。它使使用 Rspack 的感觉更像是在处理基于 webpack 的构建系统。虽然它与模块联邦兼容,但 Rsbuild 将被用来提供更简化的模块联邦体验。例如,用于 react 的 Rsbuild 插件可以自动共享默认值,或者 Rsbuild 可以提供方便的预设和模式。

我们已经开始将一些 模块联邦示例 迁移到 Rspack 和 Rsbuild。一个值得注意的例子是 CRA 迁移,该迁移非常顺利,从 CRA 切换到 Rsbuild 只用了几分钟 此处。本指南对于希望为老化构建轻松提升性能的 CRA 用户也很有帮助:Rsbuild 迁移指南。Rsbuild 也非常适合 将 Vue 示例从 vue-cli 迁移 到更快速、更轻松且更适合联邦的工具。

联邦 v1 和 v1.5 之间的区别

最初,联邦非常基础。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 },
  },
})

联邦也可以以动态方式使用,而不需要编译时插件。您可以在此处阅读有关 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);
  });

阅读有关联邦 1.5 更新的更多信息:模块联邦 1.5