Webpack 5 的 Rescue Universe 并不是官方文档中的正式 API 名称,而是对模块联邦能力的一种形象化概括。它把各自独立构建、独立部署的前端应用看作不同的运行宇宙,把远程模块加载机制比喻成救援通道。要理解这个方案,可以先回到单体前端的拆解现场:当主应用需要复用子团队的组件时,传统做法是把组件发布成 npm 包,子应用升级依赖后再重新构建,任何一个版本冲突都会拖慢整个发布流程。Webpack 5 从构建工具层面给出了不同的解法,它允许 A 应用在运行时直接加载 B 应用暴露出来的模块,B 应用可以独立部署和升级,A 应用无需重新构建即可获得新的远程代码。

这种能力背后的关键插件是 ModuleFederationPlugin。它把静态构建时固定的依赖关系拆成运行时协商,因此远程模块的加载不再是简单的脚本注入,而是包含了容器注册、共享依赖解析和版本选择的一整套流程。接下来会从远程入口、共享依赖、运行时加载和适用边界四个层面展开。
一、远程入口与本地引用配置
远程模块的暴露方通常被称为 remote。一个应用只要在 plugins 里加入 ModuleFederationPlugin,并通过 exposes 字段声明要共享的模块,就能成为远程应用。下面的配置来自一个运行在 3001 端口的组件库应用,它把 Button 组件暴露给外部。
const ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin');
module.exports = {
entry: './src/index',
mode: 'development',
devServer: {
port: 3001,
},
plugins: [
new ModuleFederationPlugin({
name: 'remoteApp',
filename: 'remoteEntry.js',
exposes: {
'./Button': './src/components/Button',
},
}),
],
};
这里的 name 是远程容器的全局标识,host 在引用它时必须使用相同的名字。exposes 的 key 表示外部导入路径,value 是本地模块路径。外部应用不会直接请求 ./src/components/Button.js,而是通过 remoteEntry.js 作为入口,找到该模块的加载方式。filename 可以自定义,默认值是 remoteEntry.js,部署时需要确保该文件能被 host 访问到。
消费远程模块的一方通常被称为 host。host 不需要把 remoteApp 的源码放进自己的构建上下文,只需要配置 remotes 字段,声明远程模块的入口地址。
const ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin');
module.exports = {
entry: './src/index',
mode: 'development',
devServer: {
port: 3002,
},
plugins: [
new ModuleFederationPlugin({
name: 'hostApp',
remotes: {
remoteApp: 'remoteApp@http://localhost:3001/remoteEntry.js',
},
}),
],
};
remotes 中的 key remoteApp 对应远程容器的 name,value 的格式为 远程名@入口地址。host 构建时会为这个远程模块创建一个占位符,代码中可以直接用 import('remoteApp/Button') 的形式动态加载。Webpack 会把这种导入拆成独立的异步 chunk,运行时才去请求远程入口。这样做的好处是,远程应用升级组件后,只要入口文件路径不变,host 应用不需要重新发布。
二、共享依赖的版本协商与单例策略
如果 host 和 remote 都各自打包了一份 React,用户在访问远程组件时,页面里可能同时存在两个 React 实例。这会导致 React 内部的 Hook 状态混乱,出现 Invalid hook call 之类的运行时错误。shared 字段正是为了解决这类公共依赖重复加载的问题。它把依赖分成共享组,运行时只加载满足要求的版本。
shared: {
react: {
singleton: true,
requiredVersion: '^18.2.0',
},
'react-dom': {
singleton: true,
requiredVersion: '^18.2.0',
},
lodash: {
singleton: false,
},
}
singleton 设置为 true 表示整个运行环境只允许存在一个该模块实例。requiredVersion 用来声明版本范围,Webpack 会比较 host、remote 各自声明的版本,如果都能满足同一版本范围,就共用一份;如果版本差异较大,会加载不同的版本并给出警告。这种协商机制比手动管理 externals 更灵活,因为每个远程应用可以继续维护自己的依赖声明,而不需要把公共依赖全部抽离到全局变量。
shared 还有一个容易忽略的属性 eager。默认情况下共享依赖是异步加载的,这有助于减少首屏体积,但会导致某些模块只能在动态 import 边界之后使用。如果某个依赖很小,并且在入口处就要被多个模块同步引用,可以把 eager 设为 true,让 Webpack 把它打进主 chunk。对于大型依赖,通常不建议开启 eager,否则会削弱代码分割的效果。版本管理依然要回到 package.json 和 package-lock.json 上,团队最好在 CI 阶段统一检查公共依赖版本,避免运行时出现难以复现的兼容问题。
三、运行时加载流程与常见故障排查
了解运行时的加载顺序,可以有效缩短远程模块出问题时的排查时间。host 页面打开后,首先会根据 remotes 配置异步请求 remoteEntry.js。这个文件体积通常很小,它负责注册远程容器,并声明哪些模块可以暴露给外部。随后当业务代码执行 import('remoteApp/Button') 时,Webpack 会向远程容器请求 Button 模块。远程容器拿到请求后,会先检查该模块依赖的共享模块是否已经加载,如果共享模块还没准备好,它会等待共享依赖的 promise 完成,再继续执行模块初始化。
import React from 'react';
import ReactDOM from 'react-dom/client';
import('remoteApp/Button').then(({ Button }) => {
const container = document.getElementById('root');
const root = ReactDOM.createRoot(container);
root.render(React.createElement(Button, { label: '来自远程的按钮' }));
});
这段代码演示了 host 如何动态使用远程组件。注意导入路径的前缀 remoteApp 必须与 remotes 配置中的 key 完全一致,后面的 ./Button 则来自远程应用 exposes 声明的 key。只要远程模块的接口保持稳定,host 就能在不感知远程实现细节的情况下完成加载。
常见故障通常集中在三个位置。一是远程入口 404,需要检查 filename 是否写对、远程应用的 devServer 或静态资源服务器是否允许跨域访问。二是远程容器加载成功但共享依赖重复,页面出现两个 React 实例,这时要检查 host 和 remote 的 shared 是否都配置了 singleton: true,并且 requiredVersion 是否兼容。三是修改远程代码后 host 没有更新,这可能是浏览器或 CDN 缓存导致,可以通过在远程入口 URL 后加版本查询参数来打破缓存。排查时建议优先在 Network 面板确认 remoteEntry.js 的响应状态,再观察 Shared module 请求是否成功。
四、Rescue Universe 方案的适用边界
模块联邦不是微前端的唯一解,也不是所有项目都适合引入。它真正发挥价值的是多团队并行开发、独立部署的大型系统,例如企业内部多个管理后台需要共享统一组件库,或者 Saas 产品需要让不同客户定制不同功能模块。在这些场景下,团队可以各自维护独立仓库、独立发布,host 无需频繁协调远程应用的版本升级。对于只有两三个页面的小项目,配置模块联邦带来的异步加载、共享协商和部署成本,可能远高于直接发布一个 npm 包。
与 iframe 方案相比,模块联邦的交互更加流畅,样式和脚本可以共享同一个窗口上下文,但也因此需要开发者自己处理样式隔离、全局变量冲突和错误兜底。与 qiankun 这类微前端框架相比,模块联邦的关注点更细粒度,它不负责应用级别的路由和生命周期,而是解决模块级别的共享问题。实际项目中可以把两者结合:qiankun 负责主应用与子应用的挂载,模块联邦负责跨应用复用组件和工具函数。
最后要强调的是,Rescue Universe 并不是一个可以一键解决所有架构问题的银弹。它更像一条救援通道,在团队需要共享模块时建立连接,但连接本身的稳定性、版本治理、监控告警仍然需要工程团队持续投入。先把 shared 版本策略和远程入口规范定清楚,再逐步把核心组件迁移到 exposes 列表,才能让这条通道真正跑起来,而不是把原本的构建问题转化成更隐蔽的运行时问题。
Webpack 5模块联邦Rescue Universe修改时间:2026-09-26 04:10:46