For AI agents: the complete documentation index is available at /zh/llms.txt, the full documentation bundle is available at /zh/llms-full.txt, and this page is available as Markdown at /zh/guides/topic-detail/module-federation/usage.md.
  • 简体中文
  • 开始使用

    在 Modern.js 中使用 Module Federation 我们推荐使用官方插件 @module-federation/modern-js-v3。

    本章节将会介绍如何通过官方插件搭含生产者应用与消费者应用,我们首先根据 Modern.js 快速上手 创建两个应用。

    安装插件

    完成应用创建后,我们分别为两个项目安装插件:

    npm
    yarn
    pnpm
    bun
    deno
    npm add @module-federation/modern-js-v3

    注册插件

    安装插件后,你需要在 modern.config.js 中注册插件:

    import { appTools, defineConfig } from '@modern-js/app-tools';
    import { moduleFederationPlugin } from '@module-federation/modern-js-v3';
    
    export default defineConfig({
      plugins: [appTools(), moduleFederationPlugin()],
    });

    生产者导出模块

    接下来,我们先修改生产者的代码,导出 Module Federation 模块。

    我们创建 src/components/Button.tsx 文件,导出一个 Button 组件:

    src/components/Button.tsx
    import React from 'react';
    
    export const Button = () => {
      return <button type="button">Remote Button</button>;
    };

    随后,在项目根目录添加 module-federation.config.ts,配置 Module Federation 模块的名称、共享依赖和导出内容:

    module-federation.config.ts
    import { createModuleFederationConfig } from '@module-federation/modern-js-v3';
    
    export default createModuleFederationConfig({
      name: 'remote',
      manifest: {
        filePath: 'static',
      },
      filename: 'static/remoteEntry.js',
      exposes: {
        './Button': './src/components/Button.tsx',
      },
      shared: {
        react: { singleton: true },
        'react-dom': { singleton: true },
      },
    });
    Tip

    在上述代码块中,我们为 Module Federation 导出的 manifest 和 remoteEntry.js 都设置了 static 前缀,这是因为 Modern.js 要求将所有需要暴露的资源都放在 static/ 目录下,Modern.js 的服务器在生产环境时也只会托管 static/ 目录。

    另外,我们还需要修改 modern.config.ts,为生产者提供一个开发环境的端口,让消费者可以通过此端口访问生产者的资源:

    modern.config.ts
    import { appTools, defineConfig } from '@modern-js/app-tools';
    import { moduleFederationPlugin } from '@module-federation/modern-js-v3';
    
    export default defineConfig({
      server: {
        port: 3051,
      },
      plugins: [appTools(), moduleFederationPlugin()],
    });

    消费者使用模块

    现在,我们修改消费者的代码,使用生产者导出的模块。

    在项目根目录添加 module-federation.config.ts,配置 Module Federation 模块的名称、共享依赖和使用的远程模块:

    module-federation.config.ts
    import { createModuleFederationConfig } from '@module-federation/modern-js-v3';
    
    export default createModuleFederationConfig({
      name: 'host',
      remotes: {
        remote: 'remote@http://localhost:3051/static/mf-manifest.json',
      },
      shared: {
        react: { singleton: true },
        'react-dom': { singleton: true },
      },
    });

    mf-manifest.json 是生产者在打包后产出的文件,包含了生产者导出的所有模块信息。

    我们创建新的路由文件 src/routes/remote/page.tsx,引入生产者模块:

    src/routes/remote/page.tsx
    import React, { Suspense, type JSX } from 'react';
    import { Button } from 'remote/Button';
    
    const Index = (): JSX.Element => {
      return (
        <div>
          <Suspense fallback={<div>Loading...</div>}>
            <Button />
          </Suspense>
        </div>
      );
    };
    
    export default Index;

    此时 remote/Button 引入会出现类型错误,这是因为本地没有远程模块的类型。Module Federation 2.0 提供了 类型提示 的能力,会在生产者构建时自动生成远程模块的类型定义,在消费者构建时自动下载。

    为此我们需要在 tsconfig.json 中添加新的 path,保证类型生效:

    tsconfig.json
    {
      "compilerOptions": {
        "paths": {
          "*": ["./@mf-types/*"]
        }
      }
    }
    Tip

    在消费者中,我们通过 remote/Button 来引用远程模块。这里简单介绍下这个路径具体代表了什么,可以先将它抽象成 [remoteAlias]/[remoteExpose]。

    第一段 remoteAlias 是生产者在消费者中的别名,它是消费者 module-federation.config.ts 中配置的 remotes 字段的 key:

    {
      remotes: {
        [remoteAlias]: '[remoteModuleName]@[URL_ADDRESS]',
      }
    }

    这里我们也将远程地址抽象为 [remoteModuleName]@[URL_ADDRESS],@ 前的部分必须对应生产者的模块名。

    第二段 remoteExpose 是生产者在 module-federation.config.ts 中配置的 exposes 字段的 key。

    启动应用

    现在,生产者应用和消费者应用都已经搭建完毕,我们可以在本地运行 modern dev 启动两个应用。

    启动后,消费者中对生产者模块的引入,不会再出现抛错,类型已经下载到消费者应用中。

    Note

    修改生产者代码后,消费者会自动拉取生产者的类型。

    访问 http://localhost:8080/remote,可以看到页面中已经包含了生产者的远程模块 Button 组件。

    我们也可以在本地执行 modern serve 来模拟生产环境。

    因为 Module Federation 插件会自动读取 Modern.js 的 output.assetPrefix 配置作为远程模块的访问地址,而该值在生产环境下构建后默认是 /。如果不做特殊处理,消费者将从自己的域名下拉取远程模块的入口文件。我们可以添加如下配置:

    import { appTools, defineConfig } from '@modern-js/app-tools';
    import { moduleFederationPlugin } from '@module-federation/modern-js-v3';
    
    // https://modernjs.dev/en/configure/app/usage
    export default defineConfig({
      server: {
        port: 3051,
      },
      output: {
        // Now this configuration is only used in the local when you run modern serve command.
        // If you want to deploy the application to the platform, use your own domain name.
        // Module federation will automatically write it to mf-manifest.json, which influences consumer to fetch remoteEntry.js.
        assetPrefix: 'http://127.0.0.1:3051',
      },
      plugins: [appTools(), moduleFederationPlugin()],
    });

    现在,在生产者中运行 modern build && MODERN_MF_AUTO_CORS=true modern serve,在消费者中运行 modern build && modern serve,即可在本地模拟生产环境,访问到远程模块。

    Tip

    在使用 modern serve 命令时,需要在启动生产者项目时携带 MODERN_MF_AUTO_CORS=true 环境变量,以自动处理跨域问题,确保消费者可以正常访问生产者的远程模块资源。

    上述用例可以参考:Modern.js & Module Federation 基础用法示例。

    相关文档