Webpack 5 发布时,官方把 Module Federation 宣传为一种让多个独立构建的应用在运行时共享代码的机制。社区有人给它起了个更形象的名字叫 Uniting Universe,也就是联合宇宙。它描述的正是宿主应用和远程应用之间不再是主从关系,而是彼此暴露模块、双向加载,形成一张动态依赖网络。这个能力颠覆了过去微前端方案必须先注册子应用、再统一编排挂载的思路。

联合宇宙的核心不是简单的代码分割,而是把构建产物中的异步模块边界扩展到了跨应用范围。Webpack 打包时会为每个配置生成一个容器入口,远程应用通过 exposes 暴露模块,宿主通过 remotes 声明要消费的地址。运行时才进行远程入口解析,构建时双方并不需要知道对方最终部署在哪。这样多个独立构建的应用可以像同一棵依赖树上的模块一样互相引用。
一、联合宇宙的容器机制与运行时加载原理
要理解 Module Federation,首先要摒弃构建时静态链接的习惯。传统微前端通常把子应用打包成完整产物,主应用在浏览器中通过 script 标签或动态 import 加载整包。Module Federation 则把共享粒度降到模块级,远程应用只需要暴露一个按钮组件或一个工具函数,宿主应用就能像调用本地模块一样调用它。
Webpack 5 在编译远程应用时会生成 remoteEntry.js,这个文件可以理解为远程模块的清单和运行时容器。它记录了 exposes 中每个模块的异步 chunk 位置、共享依赖的版本信息以及模块工厂函数的加载方式。宿主应用初始化时不会立即拉取远程模块,只有在真正执行 import('remoteApp/Button') 时才发起请求,因此首屏体积不会被无关模块拖累。
这种容器机制的关键在于 Webpack 运行时注入的全局变量和依赖协商逻辑。每个构建产物都携带自己的 webpack runtime,但共享依赖通过 shareScope 统一到一个命名空间。比如 react 和 react-dom 可以被多个远程应用和宿主共享,只有版本兼容时才会复用同一份实例,否则回退到各自打包的副本。这个协商过程在运行时完成,不需要提前锁死版本。
// remote-app/webpack.config.js
const { ModuleFederationPlugin } = require('webpack').container;
const path = require('path');
module.exports = {
entry: './src/index',
mode: 'development',
devServer: {
port: 3001,
hot: true
},
output: {
publicPath: 'http://localhost:3001/'
},
plugins: [
new ModuleFederationPlugin({
name: 'remoteApp',
filename: 'remoteEntry.js',
exposes: {
'./Button': './src/components/Button'
},
shared: {
react: { singleton: true, eager: true },
'react-dom': { singleton: true, eager: true }
}
})
]
};
二、宿主应用的 remotes 配置与动态引入
宿主应用同样需要启用 ModuleFederationPlugin,但配置重点在 remotes。remoteApp 后面的地址指向远程应用的 remoteEntry.js,Webpack 会把该地址转化为运行时容器引用。配置完成后,可以在业务代码里直接使用 import('remoteApp/Button'),而不需要在本地安装远程应用。
// host-app/webpack.config.js
const { ModuleFederationPlugin } = require('webpack').container;
module.exports = {
entry: './src/index',
mode: 'development',
devServer: {
port: 3000
},
plugins: [
new ModuleFederationPlugin({
name: 'hostApp',
remotes: {
remoteApp: 'remoteApp@http://localhost:3001/remoteEntry.js'
},
shared: {
react: { singleton: true, eager: true },
'react-dom': { singleton: true, eager: true }
}
})
]
};
这里存在一个经常被忽略的点:remotes 的键名 remoteApp 必须与远程应用配置的 name 一致,否则运行时无法匹配命名空间。地址前面的 remoteApp 是全局变量前缀,Webpack 会将其包装成容器引用。上面的 import('remoteApp/Button') 实际等价于先加载 remoteEntry.js,再从 window.remoteApp 这个容器中获取 Button 模块。
动态引入远程组件时,需要用 React.lazy 或手动处理 Promise。以 React 为例,可以写一个封装函数把 import 映射为懒加载组件。注意远程模块返回的结构通常是 { default: Component },如果远程应用用 ESM 导出,需要判断模块类型和 default 属性。
import React, { lazy, Suspense } from 'react';
const RemoteButton = lazy(() =>
import('remoteApp/Button').then((module) => ({
default: module.default || module.Button
}))
);
export default function App() {
return (
<Suspense fallback={<div>加载远程按钮中...</div>}>
<RemoteButton />
</Suspense>
);
}
三、共享依赖策略与版本冲突的治理
共享依赖是联合宇宙能不能稳定运行的核心。Webpack 5 的 shared 配置不止是告诉双方哪些包可以复用,它定义了一套运行时协商机制。singleton 为 true 时,整个页面上只允许存在一个该依赖的实例,冲突时通过版本比较决定谁提供实例,其他方使用消费方提供的版本。如果设置为 false 或未配置,Webpack 会按版本范围尝试共享,但无法满足时各自加载自己的副本。
例如 react 是典型需要 singleton 的库。不同版本同时存在会让 hooks 状态错乱,甚至触发 Invalid hook call 错误。而像 lodash 这样无状态工具库可以不做 singleton,只需要 requiredVersion 指定最低版本。如果宿主和远程应用的 lodash 版本兼容,运行时就会复用远程应用的 chunk,否则回退到本地 chunk。
shared: {
react: {
singleton: true,
requiredVersion: '^18.2.0',
eager: true
},
lodash: {
requiredVersion: '^4.17.21'
}
}
eager 参数也容易让人困惑。eager: true 表示该依赖会在应用启动时立即加载并加入共享作用域,适合宿主应用作为提供方的场景。如果宿主和远程应用同时对 react 设置 eager: true,在 Webpack 5 的某些版本中会导致共享冲突,尤其在单一入口的宿主中。通常建议宿主应用对核心框架设置 eager,远程应用无需设置 eager。
当出现版本不兼容时,控制台会给出模块共享冲突警告。解决思路不是强行降低版本,而是通过 package.json 的 overrides 字段或 yarn resolutions 统一版本,再从共享配置层面约束 requiredVersion。如果业务确实需要两个大版本并存,就应该把其中一个依赖改成非 singleton,并接受双实例带来的体积增加。
四、联合宇宙模式下的性能优化与部署注意点
虽然 Module Federation 能显著降低重复代码,但 remoteEntry.js 的加载和远程模块的按需请求会引入额外的网络往返。远程入口通常很小,但浏览器需要先下载并执行 remoteEntry.js 才能知道具体 chunk 位置,所以远程模块的首次加载会比本地模块多一次协商。优化方式包括对 remoteEntry.js 设置长缓存、使用 HTTP/2 多路复用减少连接开销,以及把远程入口地址收敛到同一域名下避免跨域和 DNS 查询。
部署时还要注意 remoteEntry.js 的文件名不要随意更改,因为宿主引用的地址依赖这个文件名。如果远程应用发版时把 remoteEntry 改成带 hash 的名称,宿主没有同步修改就会加载失败。保持 filename: 'remoteEntry.js' 不变,同时在构建产物中保留该入口文件即可。对于多环境部署,可以通过环境变量替换 remotes 地址,让同一份宿主导出配置适配测试、预发和生产。
const remoteUrl =
process.env.NODE_ENV === 'production'
? 'https://cdn.ipipp.com/remoteEntry.js'
: 'http://localhost:3001/remoteEntry.js';
new ModuleFederationPlugin({
name: 'hostApp',
remotes: {
remoteApp: `remoteApp@${remoteUrl}`
}
});
联合宇宙架构适合多个团队并行开发、技术栈不要求完全统一的场景。它把发布粒度从整个应用缩小到模块,团队可以独立部署组件而不影响宿主发版。但这种灵活也带来治理成本:共享依赖版本漂移、远程模块加载失败时的降级处理、样式隔离等都需要提前规划。真正落地时,建议从一两个低风险组件开始,逐步抽象共享层,让多个应用在同一个联合宇宙中稳定协作。
Webpack 5Module Federation微前端修改时间:2026-09-29 21:58:29