Webpack 5 相比 Webpack 4 最大的变化,除了持久化缓存和更好的 Tree Shaking,还有一系列围绕 Individuality 个性展开的改进。这个概念听起来抽象,但它解决的是前端工程里一个老大难问题:当代码来自不同的团队、不同的构建流程、不同的容器时,如何保证每一份代码都能保持自己的独立性,同时又能安全地共享公共资源。本文将从模块联邦切入,逐步拆解 Individuality 在 Webpack 5 中的具体体现和实际用法。

什么是 Individuality:从模块身份说起
在 Webpack 4 时代,所有模块都被塞进一个统一的全局作用域中,模块 ID 由数字递增生成。这种做法在小项目里没什么问题,一旦多个独立构建的产物被拼到同一个页面上,就会互相干扰:模块 ID 冲突、全局变量覆盖、样式互相污染。所谓 Individuality,指的是 Webpack 5 给每个构建单元(模块、chunk、容器)赋予了独立且确定的身份标识,使得多个构建产物可以在运行时共存而不打架。
具体来说,Webpack 5 用基于内容哈希的 moduleIds: "deterministic" 替代了旧的数字 ID,同时引入了 chunk ID 的确定性算法。这意味着同一份源码在任何机器上构建出来的模块 ID 都是一致的,这正是远程模块能够被可靠引用的前提。你可以理解为,Webpack 4 的模块是流水线上的无名工位,而 Webpack 5 给每个工位挂上了独一无二的工牌。
这个身份系统还延伸到了运行时层面。Webpack 5 将原本混在一起的 runtime 代码拆分出来,可以通过 optimization.runtimeChunk 配置独立成文件,多个容器之间可以引用同一个 runtime,也可以各自持有独立的 runtime,灵活性大大提升。
模块联邦:Individuality 最典型的应用场景
模块联邦(Module Federation)是 Webpack 5 的招牌功能,也是 Individuality 思想最集中的体现。它允许一个 JavaScript 应用在运行时动态加载另一个独立构建的应用中的模块,宿主与远程之间通过协商好的共享依赖来避免重复加载。
下面是一个典型的宿主方配置:
const ModuleFederationPlugin = require("webpack/lib/container/ModuleFederationPlugin");
module.exports = {
// 宿主应用对外暴露的名称,是它的个性标识
output: {
publicPath: "http://localhost:3000/",
},
plugins: [
new ModuleFederationPlugin({
name: "host",
remotes: {
// 引用另一个独立构建的远程应用
remoteApp: "remoteApp@http://localhost:3001/remoteEntry.js",
},
shared: {
react: { singleton: true },
"react-dom": { singleton: true },
},
}),
],
};远程方的配置与之对称,通过 exposes 把内部模块暴露出去。这里的关键在于:宿主和远程是两个完全独立的构建,各自有自己的 name、自己的依赖版本策略,这正是 Individuality 的落地。在 Webpack 4 里,你想实现类似的效果,得借助 externals 加约定,一旦版本不一致就直接崩溃。
shared 配置里的 singleton: true 值得单独说一说。它告诉 Webpack:React 这类库在整条依赖链上只允许存在一个实例。如果远程应用带了不同版本的 react,联邦机制会尝试协商出一个满足所有 consumers 版本范围的实例,协商失败时给出警告。这种按需协商而非强制统一的做法,比全局 externals 精细得多。
样式隔离与运行时独立性:容易被忽视的部分
Individuality 不只作用于 JS 模块,也影响样式和运行时行为。微前端场景下最常见的翻车现场就是样式冲突:远程应用的全局 CSS 把宿主的按钮染成了奇怪的紫色。Webpack 5 虽然没有内置完整的样式隔离方案,但它提供了必要的构建基础设施,配合 CSS Modules 或 postcss 前缀插件,可以让每个容器的样式天然带有个体标识。
一个实用做法是给每个子应用配置独立的 CSS Modules localIdentName:
module.exports = {
module: {
rules: [
{
test: /\.css$/,
use: [
"style-loader",
{
loader: "css-loader",
options: {
modules: {
// 用应用名作为类名前缀,保证个体性
localIdentName: "[name]__[local]__[hash:base64:5]",
exportLocalsConvention: "camelCase",
},
},
},
],
},
],
},
};另一个容易踩的坑是 runtime 冲突。当页面上同时存在多个联邦容器时,如果不共享 runtime,每个容器都会加载一份 webpack runtime,体积有所增加,但胜在互不干扰;如果选择共享,则必须保证所有容器的 Webpack 大版本一致。建议在团队规范里明确约定共享策略,别让各子应用自行其是。
升级建议与性能权衡
开启确定性模块 ID 和模块联邦后,构建产物会有一些变化:持久化缓存的命中率显著提高,二次构建速度通常能提升一大截;但运行时的协商逻辑会带来少量额外代码,单看首屏体积可能增加几 KB,换来的是多应用协作时的大量重复依赖被消除,整体上是划算的。
升级时有几个检查点值得注意。第一,确认 optimization.moduleIds 和 optimization.chunkIds 使用了 deterministic(生产模式下默认开启),不要手动改回 size 或旧的 hashed。第二,如果项目里有代码依赖数字模块 ID(比如脏写 __webpack_modules__),必须提前清理。第三,共享依赖要控制版本范围写法,requiredVersion 与 strictVersion 的组合用错了会导致运行时静默加载旧版本,排查起来相当费劲。
总的来说,Individuality 是 Webpack 5 把工程能力从单应用推向多应用协作的核心设计。理解了模块身份、容器独立性和共享协商这三层逻辑,再去配置模块联邦就不是照抄文档,而是真正知道自己每一行配置在做什么。
Webpack 5模块联邦Individuality修改时间:2026-09-17 00:02:39