一、核心结论:模块联邦的本质
模块联邦( )是 5引入的一种运行时()模块共享机制。 其核心价值在于:让多个独立构建、独立部署的前端应用,能在浏览器端直接相互引用和共享模块,而无需通过npm包的形式进行编译时集成。
核心特征:
运行时加载:模块的引用发生在浏览器运行时,而非编译打包阶段
独立部署:每个应用(或称为“容器”)可独立开发、独立部署、独立版本管理
去中心化:不存在中央仓库,任意应用既可成为“被依赖方”,也可成为“依赖方”
与npm包的本质区别:
| 对比维度 | npm包方式 | 模块联邦 |
|---|---|---|
| 集成时机 | 编译时 | 运行时 |
| 部署方式 | 所有应用需同步升级依赖 | 独立部署,各应用版本独立 |
| 模块更新 | 需重新构建所有依赖应用 | 宿主应用无需重新构建 |
| 代码耦合 | 编译时绑定,强耦合 | 运行时动态加载,弱耦合 |
> 信息来源: 5官方文档 –
二、模块联邦的完整运行流程
模块联邦的运行分为两个阶段:构建阶段和运行时阶段。
2.1 构建阶段:容器()的生成
在构建时,通过配置in插件,会为当前应用生成一个特殊的入口文件——容器()。
构建阶段的核心工作:
1. 生成容器入口文件
会将当前应用中所有被标记为“暴露”的模块,收集并生成一个.js文件。该文件包含:
暴露模块的映射表(模块名称 → 模块ID)
模块加载器函数
共享依赖的版本信息
2. 确定依赖关系
通过配置中的字段,声明当前应用可供其他应用使用的模块;通过字段,声明当前应用需要从哪些远程应用获取模块。
3. 处理共享依赖
通过字段,声明哪些依赖模块(如react、vue)需要在多个应用间共享,避免重复加载。
配置示例:
// ..js(应用A:容器)
const in = ('/lib//in');
. = {
: [
new in({
name: 'appA', // 容器名称
: '.js', // 容器入口文件名
: {
'./': './src/', // 暴露的模块
'./': './src/'
},
: {
react: { : true }, // 共享依赖
'react-dom': { : true }
}
})
]
};
2.2 运行时阶段:模块的动态加载与执行
当浏览器加载宿主应用(使用远程模块的应用)时,模块联邦的运行时流程如下:
步骤1:加载远程容器入口
宿主应用执行.js,在浏览器环境中注册一个全局对象(以name配置命名,如appA),该对象提供了获取远程模块的接口。
步骤2:加载远程模块
当宿主应用代码执行到需要远程模块的位置(例如('appA/')),模块联邦运行时会执行以下操作:
1. 检查该远程模块是否已被加载,若已加载则直接返回
2. 若未加载,则通过动态标签加载远程模块对应的chunk文件
3. 执行远程模块代码,并返回模块的导出内容
步骤3:共享依赖的协调
模块联邦运行时会执行依赖去重逻辑:
对于中声明的依赖,运行时会检查当前环境中是否已存在该依赖
若存在且版本兼容,则复用已有版本,不再加载远程应用的依赖
若不存在或版本不兼容,则加载远程应用自带的依赖版本
步骤4:模块缓存
所有加载过的远程模块会被缓存在内存中,同一模块在单个页面生命周期内只会加载一次。
> 信息来源:官方文档 –
三、核心原理解析:关键技术细节
3.1 容器对象()的结构
每个模块联邦应用在运行时都会暴露一个全局容器对象,其核心结构为:
.appA = {
// 获取模块的方法
get: () => {
new ((, ) => {
// 加载模块的chunk
// 返回模块导出
});
},
// 初始化方法,用于共享依赖
init: () => {
// 将传入的共享依赖作用域合并到当前容器
}
};
3.2 模块加载的核心机制:异步chunk + 动态
模块联邦并未使用 5新增的特殊技术,而是基于已有的异步chunk加载机制和动态标签注入来实现:
1. 异步chunk生成:被暴露的模块会被单独打包成独立的chunk文件
2. 动态注入:运行时通过.l方法动态创建<>标签加载chunk
3. 模块执行:chunk加载完成后,模块代码在全局作用域下执行,将导出内容挂载到容器对象的get方法返回值中
3.3 依赖共享的核心:共享作用域()
共享依赖的协调是通过“共享作用域”机制实现的:
1. 共享作用域的定义:每个容器应用在初始化时,会创建一个共享作用域对象,记录当前已加载的共享依赖及其版本
2. 依赖解析流程:
当容器A需要加载一个共享依赖(如react)时,会先检查当前共享作用域中是否已存在react
若存在,且版本满足要求,则直接使用
若不存在,则加载容器A自身的react版本,并将其注册到共享作用域
3. 版本冲突处理:通过配置中的、、等字段来控制版本冲突时的行为
共享配置关键字段说明:
| 字段 | 作用 |
|---|---|
|
强制整个应用环境只使用单一版本 |
|
指定需要的依赖版本范围 |
|
版本不匹配时抛出错误而非降级 |
3.4 远程模块的加载流程代码示意
// 宿主应用中加载远程模块的伪代码实现
async (, ) {
// 1. 检查远程容器是否已初始化
if (![]) {
// 2. 加载远程入口文件
await (${}/.js);
}
// 3. 初始化共享依赖
await [].init(es__.);
// 4. 获取模块
const = await [].get();
const = ();
;
}
> 信息来源:源码 – .js, .js
四、常见问题与注意事项
4.1 版本冲突问题
现象:宿主应用使用react 18,远程应用使用react 17,共享配置不当会导致运行时错误。
解决方案:
统一核心依赖版本,或使用: true强制单例
在中明确配置,让在构建时进行版本检查
: {
react: {
: true,
: '^18.0.0'
}
}
4.2 跨域问题
现象:远程入口文件或chunk文件跨域请求被浏览器拦截。
解决方案:
远程资源服务器需配置CORS头:--Allow-: *(或指定域名)
使用标签加载时,确保远程资源支持跨域请求
4.3 远程应用变更后的缓存问题
现象:远程应用更新后,宿主应用仍加载旧版本chunk。
解决方案:
使用配置时,可加入hash值,如.[].js
宿主应用需实现远程入口URL的动态更新机制
推荐结合h__动态配置远程资源路径
4.4 类型安全
现象:环境下,远程模块的接口变更无法被宿主应用感知,导致运行时错误。
解决方案:
使用配合模块声明文件(.d.ts)进行类型定义
远程应用通过暴露模块时,同时提供类型定义文件
使用工具如@-/自动生成类型声明
4.5 性能优化建议
关键指标:远程chunk的加载不应阻塞首屏渲染
优化方法:
使用()动态导入远程模块,而非顶层导入
对非首屏必需的远程模块,采用懒加载策略
通过或预加载关键远程模块
// 推荐:按需加载
.('click', async () => {
const = await ('appA/');
// 使用
});
五、总结:模块联邦的技术定位
模块联邦解决了前端微前端架构中“独立部署”与“运行时集成”的核心矛盾。其技术实现基于现有的异步加载能力,通过在构建阶段生成容器入口、在运行时通过共享作用域协调依赖,实现了跨应用的模块共享。
适用场景:
大型应用的微前端拆分
多团队协作的独立开发与部署
需要按需加载第三方模块的场景
不适用场景:
对首屏性能要求极致的简单应用(引入额外运行时开销)
需要严格隔离运行时环境的场景(模块联邦共享全局作用域)
> 信息来源: 5官方文档、微前端架构实践(2023)

