Module Federation2概念
模块联合是一种 JavaScript 应用去中心化的架构模式(类似于服务器端的微服务)。它允许您在多个 JavaScript 应用程序(或微前端)之间共享代码和资源。这可以帮助您:
-
减少代码重复
-
提高代码可维护性
-
降低应用程序的整体大小
-
增强应用程序的性能
module federation
v1和v2的区别是什么?
Module Federation 2.0 与 Webpack5 内置的 Module Federation 不同,它不仅提供模块导出、加载和依赖共享等核心功能,还提供额外的动态类型提示、Manifest、Federation Runtime 和 Runtime Plugin System。这些特性使得 Module Federation 更适合用作大型 Web 应用程序中的微前端架构。
特点
- 代码共享、依赖复用
- 资源清单(Manifest)
- module federation runtime
- 运行时插件系统(tapable实现)
- 动态类型提示
- Chrome 开发工具
- Rspack 和 Webpack 支持
模块联邦并没有样式隔离机制, 这意味着, 当主子应用很有可能会互相造成样式污染
概念理解:生产者
通过 Module federation 构建插件设置了 exposes
暴露其他模块给其他 JavaScript 应用消费的应用在 Module federation 中称之为 Provider(生产者),生产者可以同时作为一个消费者。
概念理解:消费者
通过 Module federation 构建插件设置了 remotes
消费其他生产者的模块称之为 Consumer (消费者),消费者可以同时作为一个生产者。
@module-federation/enhanced
Module Federation 核心包,作为 Webpack 构建插件、 Rspack 构建插件、Runtime 入口依赖。
@module-federation/runtime
Module Federation 的 Runtime 包,通常使用 @module-federation/enhanced 来使用 Runtime 能力,若不需要使用构建工具时可单独安装此包。
概念目标
-
它既可以暴露,又可以使用 webpack 支持的任何模块类型
-
代码块加载应该并行加载所需的所有内容(web:到服务器的单次往返)
-
从使用者到容器的控制
- 重写模块是一种单向操作
- 同级容器不能重写彼此的模块。
-
概念适用于独立于环境
- 可用于 web、Node.js 等
-
共享中的相对和绝对请求
- 会一直提供,即使不使用
- 会将相对路径解析到
config.context
- 默认不会使用
requiredVersion
-
共享中的模块请求
- 只在使用时提供
- 会匹配构建中所有使用的相等模块请求
- 将提供所有匹配模块
- 将从图中这个位置的 package.json 提取
requiredVersion
- 当你有嵌套的 node_modules 时,可以提供和使用多个不同的版本
-
共享中尾部带有
/
的模块请求将匹配所有具有这个前缀的模块请求
使用
目前 Module Federation 提供了两种注册模块和加载模块的方式:
-
一种是在构建插件中声明(一般是在 module-federation.config.ts 文件中声明)
-
另一种方式是直接通过 runtime 的 api 进行模块注册和加载。
两种模式并不冲突可结合使用。你可以根据你的实际场景灵活选取模块注册方式和时机
运行时注册模块和构建配置注册模块的区别如下:
运行时注册模块 | 插件中注册模块 |
---|---|
可脱离构建插件使用,在 webpack4 等项目中可直接使用纯运行时进行模块注册和加载 |
构建插件需要是 webpack5 或以上 |
支持动态注册模块 | 不支持动态注册模块 |
不支持 import 语法加载模块 |
支持 import 同步语法加载模块 |
支持 loadRemote 加载模块 |
支持 loadRemote 加载模块 |
设置 shared 必须提供具体版本和实例信息 |
设置 shared 只需要配置规则即可,无须提供具体版本及实例信息 |
shared 依赖只能供外部使用,无法使用外部 shared 依赖 |
shared 依赖按照特定规则双向共享 |
可以通过 runtime 的 plugin 机制影响加载流程 |
目前不支持提供 plugin 影响加载流程 |
不支持远程类型提示 | 支持远程类型提示 |
插件用法
快速创建项目
Module Federation 提供了 create-module-federation
工具来创建项目,不需要全局安装,直接使用 npx 按需运行即可。
模板
在创建项目时,你可以选择 create-module-federation
提供的下列模板:
模板 | 描述 |
---|---|
provider-modern | 使用 Modern.js 的生产者 |
provider-rsbuild | 使用 Rsbuild 的生产者 |
provider-rslib | 使用 Rslib 的生产者 |
provider-rslib-storybook | 使用 Rslib 的生产者,并且开启了 storybook 功能 |
consumer-modern | 使用 Modern.js 的消费者 |
consumer-rsbuild | 使用 Rsbuild 的消费者 |
生产者, 提供组件
bash
# 创建 my-project目录下创建mf-provider的生产者
npx create-module-federation --dir my-project --template provider-modern --name mf-provider
js
import { createModuleFederationConfig } from "@module-federation/rsbuild-plugin";
export default createModuleFederationConfig({
name: "mf-provider",
exposes: {
"./Provider": "./src/components/ProviderComponent.tsx",
},
shared: {
react: { singleton: true },
"react-dom": { singleton: true },
},
});
消费者, 使用生产者
bash
# 创建 my-project目录下创建mf-consumer的消费者
npx create-module-federation --dir my-project --template consumer-modern --name mf-consumer
js
import { createModuleFederationConfig } from "@module-federation/rsbuild-plugin";
export default createModuleFederationConfig({
name: "mf-consumer",
remotes: {
provider: "mf-provider@http://localhost:3001/mf-manifest.json",
},
shareStrategy: "loaded-first",
shared: {
react: { singleton: true },
"react-dom": { singleton: true },
},
});
import Provider from "provider/Provider";
<Provider></Provider>;
runtime用法
module federation v2
允许我们在写运行时代码去加载远程模块,但是远程模块的导出还是交给了构建工具基于@module-federation/runtime
去实现
这个git项目内还有很多使用案例
模块加载
tsx
// 如果没有使用构建插件,那么可以创建新的实例,并注册模块
import { createInstance } from '@module-federation/enhanced/runtime';
import React from 'react';
const mf = createInstance({
name: 'mf_host',
remotes: [
{
name: 'remote1',
alias: 'remote-1',
entry: 'http://localhost:3001/mf-manifest.json',
}
]
});
export default () => {
const MyButton = React.lazy(() =>
mf.loadRemote('remote1').then(({ MyButton }) => {
return {
default: MyButton
};
}),
);
return (
<React.Suspense fallback="Loading Button">
<MyButton />
</React.Suspense>
);
}
原理

-
通过share的模块回自动在执行链路过程中生成
share scope
,而本地host和remote都会进行提供相对应的modules, 在进行使用的时候通过校验share scope
中的provided模块是否符合当前的版本等信息,如果符合则进行加载模块并存储,从而达到了共享模块的母的 -
模块加载是通过异步加载,只有在使用的时候才会进行加载对应的模块内容,在未加载前只存储对应的相关模块属性信息