在微前端架构里,每个子应用独立开发、独立部署,却又要共享组件、避免重复打包。Webpack 5 的 Module Federation 把这一诉求变成了"一行配置 + 一次 HTTP 请求"就能完成的事。
一、为什么需要模块联邦
传统微前端方案常把公共库打包进每个子应用,导致:
- 同一份
lodash
在 A、B、C 项目里出现三次 - 子应用升级依赖需全量重新部署
- 运行时版本冲突难以检测
Module Federation 把这些痛点抽象成 "远程模块" 概念:共享代码不再属于任何一方,而是成为按需拉取的独立产物。
二、核心思想
每个微前端都是一个 联邦(Federation),拥有:
- name:全局唯一标识,如
home
、active
- exposes:向外提供的模块列表
- remotes:需要消费的远程联邦地址
- shared:共享依赖,避免重复加载
联邦之间通过 HTTP 动态加载对方的入口文件,实现真正的运行时共享。
三、实战:两个独立工程的互相引用
场景
home
工程:提供实时时钟组件now.js
active
工程:提供新闻列表组件news.js
- 两者都使用
jquery
- home 工程暴露 now
js
// home/webpack.config.js
const ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin')
module.exports = {
plugins: [
new ModuleFederationPlugin({
name: 'home',
filename: 'home-entry.js',
exposes: { './now': './src/now.js' },
shared: { jquery: { singleton: true } }
})
]
}
访问 http://localhost:8080/home-entry.js
即可得到联邦入口。
- active 工程消费 now
js
// active/webpack.config.js
const ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin')
module.exports = {
plugins: [
new ModuleFederationPlugin({
name: 'active',
filename: 'active-entry.js',
remotes: { home: 'home@http://localhost:8080/home-entry.js' },
exposes: { './news': './src/news.js' },
shared: { jquery: { singleton: true } }
})
]
}
在代码中直接远程引入:
js
import now from 'home/now'
Webpack 会在运行时去 8080
拉取 home-entry.js
,并缓存解析结果。
- 共享依赖
shared: { jquery: { singleton: true } }
声明jquery
为单例:
- 两个联邦共用一份运行时实例
- 版本不一致时,Webpack 会抛出运行时警告,避免静默冲突
四、运行时流程揭秘
- 浏览器加载
active-entry.js
- 发现
home/now
依赖,异步请求http://localhost:8080/home-entry.js
- home 联邦把
now.js
模块注入全局变量home
- active 联邦通过
window.home.get('./now')
拿到模块引用 - 模块代码与依赖
jquery
一并执行,页面显示实时时钟
整个过程零打包冗余,零版本冲突,零额外脚本。
五、迁移与最佳实践
- 渐进式改造:旧项目保持独立构建,只需新增联邦暴露与远程声明即可接入共享生态
- 共享策略:将 UI 组件库、工具函数、通用业务模块放入联邦,主应用专注业务差异化
- 版本管理:通过
requiredVersion
字段声明兼容范围,防止破坏性升级 - CDN 加速:联邦入口文件与产物部署到 CDN,实现全球低延迟拉取
结语
模块联邦把微前端的"代码共享"从构建时难题转化为运行时协议:
- 暴露方像发布 npm 包一样声明模块
- 消费方像 import 本地文件一样引用模块
- Webpack 负责版本协商、冲突检测与按需加载