Webpack 5 发布以来,官方列出的新特性包括模块联邦、持久化缓存、资源模块、更好的 Tree Shaking 等,其中并没有一个叫 Democracy 民主 的正式功能。这个说法更多来自社区对 Webpack 5 模块联邦设计理念的形象概括:它让多个独立构建的应用可以像民主协商一样共享代码,而不需要一个中心化的部署单元。下面从这一比喻出发,梳理 Webpack 5 中真正值得关注的去中心化能力。

从 Democracy 民主到模块联邦:一个非官方说法的来源
如果翻阅 Webpack 官方文档,完全找不到 Democracy 这个特性名称。官方在介绍 Webpack 5 时,重点强调的是 Module Federation,也就是模块联邦。模块联邦允许不同 Webpack 构建之间在运行时动态加载对方的模块,每个构建既可以对外暴露组件,也可以从其他构建中引入组件。这种模式下没有谁一定是中心,也没有谁必须依赖某个全局的部署平台,每个应用都保留自己的构建、部署和发布节奏。
社区之所以把模块联邦类比为民主,是因为它改变了传统微前端架构中常见的集中式方案。过去的微前端往往需要一个主应用或者一个中央注册表来协调子应用,模块联邦则更像一种平等的协作关系。每个应用可以自主决定暴露什么、消费什么,共享依赖的版本通过配置协商,而不是由某个中心强制规定。虽然官方并没有使用这个名称,但理解这个比喻有助于把握模块联邦的设计初衷。
从技术角度看,这种去中心化并非没有约束。模块联邦仍然依赖 Webpack 运行时来协调模块加载,remote 应用需要提供一个入口文件供 host 应用加载,共享依赖也需要在配置中声明。所谓民主并不是完全无序,而是把协调成本从构建期转移到了运行时,让团队在部署层面获得更大的独立性。
模块联邦如何体现民主设计:host 与 remote 的协作方式
模块联邦的典型配置分为两方:暴露模块的一方通常称为 remote,消费模块的一方称为 host。一个应用可以同时扮演 host 和 remote。比如下面这个配置,一个应用通过 ModuleFederationPlugin 暴露了一个 Button 组件,同时声明了自己会共享 react 和 react-dom 依赖。
const ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin');
module.exports = {
entry: './src/index',
mode: 'development',
devServer: { port: 3001 },
plugins: [
new ModuleFederationPlugin({
name: 'app1',
filename: 'remoteEntry.js',
exposes: {
'./Button': './src/Button'
},
shared: {
react: { singleton: true },
'react-dom': { singleton: true }
}
})
]
};
在 host 应用中,可以直接使用动态 import 从 remote 的公开路径加载组件,而不需要把组件源码复制到本地项目。下面的代码展示了如何在 React 中消费 app1 暴露的 Button 组件。
import React from 'react';
const RemoteButton = React.lazy(() => import('app1/Button'));
function App() {
return (
<React.Suspense fallback="Loading...">
<RemoteButton />
</React.Suspense>
);
}
export default App;
从这段代码可以看出,host 应用在构建时并不需要知道 Button 组件的具体实现,只需要在运行时通过 remoteEntry.js 获取模块信息。这种机制让不同团队可以独立发布组件,只要保持 exposed 路径和共享依赖约定不变,host 应用不需要重新构建就能拿到最新的 remote 代码。这种协作模式正是社区所说的民主特性的来源之一。
不过模块联邦的共享依赖并不是简单地把 react 打包进每个应用,而是通过 shared 配置让多个应用复用同一个依赖实例。配置中 singleton: true 表示在整个运行时环境中只允许存在一个 react 实例,避免出现多个 React 副本导致的 Hook 状态异常等问题。如果两个应用声明的 react 版本不一致,Webpack 会尝试按兼容规则选择版本,必要时发出警告。这种版本协商机制也体现了模块联邦在独立性之外对一致性的关注。
除了模块联邦,Webpack 5 还有哪些降低门槛的民主化改进
Webpack 5 真正带来的另一个重要变化是持久化缓存。以前的 Webpack 构建缓存主要依赖外部工具或 loader 级别的缓存,Webpack 5 原生支持了基于文件系统的缓存,通过设置 cache.type 为 filesystem 就可以让二次构建大幅提速,尤其是大型项目中效果非常明显。
module.exports = {
cache: {
type: 'filesystem'
},
module: {
rules: [
{
test: /\.png$/,
type: 'asset/resource'
}
]
}
};
另一个改进是资源模块的引入。Webpack 4 处理图片、字体等静态资源通常需要 raw-loader、file-loader、url-loader 等额外依赖,Webpack 5 把这些能力内置成了 asset/resource、asset/inline、asset/source 和 asset 四种模块类型。上面的配置中,png 文件直接使用 asset/resource 类型,Webpack 会自动生成文件并返回 URL,不再需要安装额外的 loader。这个变化让配置更加简洁,也减少了项目依赖数量。
Webpack 5 还加强了配置校验,对于常见的拼写错误、无效选项会给出更清晰的错误提示。同时它对 Tree Shaking 的优化更加稳健,支持了嵌套模块的副作用分析。这些改进虽然和模块联邦没有直接关系,但都体现了降低使用门槛、让更多开发者能自主配置构建流程的方向,和民主这个词背后的平等参与意味是一致的。
落地模块联邦时需要注意的误区和实践建议
尽管模块联邦设计上鼓励去中心化,但在实际项目中不能把它理解为完全不需要治理。如果多个 remote 应用共享同一个依赖,却没有统一版本策略,运行时很容易出现版本冲突或者重复加载的问题。因此建议团队在启用模块联邦之前,先明确共享依赖清单,约定主版本范围,并尽量使用 singleton 配置来保证关键依赖的一致性。
另一个常见误区是把所有组件都暴露出去。过度暴露会让 remote 入口体积变大,也会增加模块之间的耦合。合理的做法是只暴露那些真正需要跨应用复用的组件或工具函数,比如通用按钮、表格、鉴权逻辑等,业务私有模块继续保留在各自应用内部。这样既能享受模块联邦带来的灵活性,又不会让架构变得难以维护。
远程入口加载失败也是需要提前考虑的问题。由于 host 应用在运行时才拉取 remoteEntry.js,如果 remote 服务不可用,页面可能会出现空白或报错。可以在动态 import 外面增加错误边界或降级逻辑,比如加载失败时渲染本地兜底组件。Webpack 5 本身不会自动处理这类运行时异常,需要开发者在应用层做好容错设计。
最后需要明确,在正式技术沟通中应该使用 Module Federation 这个官方名称,而不是 Democracy 民主。后者只是一个帮助理解的比喻,如果直接把它当作 Webpack 5 的官方特性,容易在团队内部造成误解。理解比喻背后的设计思想,再去查阅官方文档和配置示例,才能真正掌握模块联邦的用法。