logo
  • 指南
  • 配置
  • 插件
  • API
  • 示例
  • 社区
  • Modern.js 2.x 文档
  • 简体中文
    • 简体中文
    • English
    • 开始
      介绍
      快速上手
      版本升级
      名词解释
      技术栈
      核心概念
      页面入口
      构建工具
      Web 服务器
      基础功能
      路由
      路由基础
      配置式路由
      数据管理
      数据获取
      数据写入
      数据缓存
      渲染
      服务端渲染(SSR)
      服务端流式渲染(Streaming SSR)
      渲染缓存
      静态站点生成(SSG)
      渲染预处理 (Render Preprocessing)
      样式开发
      引入 CSS
      使用 CSS Modules
      使用 CSS-in-JS
      使用 Tailwind CSS
      HTML 模板
      引用静态资源
      引用 JSON 文件
      引用 SVG 资源
      引用 Wasm 资源
      调试
      数据模拟(Mock)
      网络代理
      使用 Rsdoctor
      使用 Storybook
      测试
      Playwright
      Vitest
      Jest
      Cypress
      路径别名
      环境变量
      构建产物目录
      部署应用
      进阶功能
      使用 Rspack
      使用 BFF
      基础用法
      运行时框架
      扩展 BFF Server
      扩展一体化调用 SDK
      文件上传
      跨项目调用
      优化页面性能
      代码分割
      静态资源内联
      产物体积优化
      React Compiler
      提升构建性能
      浏览器兼容性
      配置底层工具
      源码构建模式
      服务端监控
      Monitors
      日志事件
      指标事件
      国际化
      基础概念
      快速开始
      配置说明
      语言检测
      资源加载
      路由集成
      API 参考
      高级用法
      最佳实践
      自定义 Web Server
      专题详解
      模块联邦
      简介
      开始使用
      应用级别模块
      服务端渲染
      部署
      集成国际化能力
      常见问题
      依赖安装问题
      命令行问题
      构建相关问题
      热更新问题
      已下线功能
      📝 编辑此页面
      上一页简介下一页应用级别模块

      #开始使用

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

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

      #安装插件

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

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

      #注册插件

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

      import { appTools, defineConfig } from '@modern-js/app-tools';
      import { moduleFederationPlugin } from '@module-federation/modern-js';
      
      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';
      
      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';
      
      export default defineConfig({
        dev: {
          port: 3051,
        },
        plugins: [appTools(), moduleFederationPlugin()],
      });

      #消费者使用模块

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

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

      module-federation.config.ts
      import { createModuleFederationConfig } from '@module-federation/modern-js';
      
      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, { useState, Suspense } 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';
      
      // 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 serve,在消费者中运行 modern build && modern serve,即可在本地模拟生产环境,访问到远程模块。

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

      #相关文档

      • Module Federation 官方文档