Webpack 5 发布至今已经相当成熟,但团队在讨论升级方案时,仍经常冒出一些来源不明的特性名称,比如所谓充满希望宇宙这类听起来很科幻的说法,实际上 Webpack 5 官方更新日志里根本不存在这个名字。与其被这些以讹传讹的信息干扰,不如回到源码和官方文档,把真正落地的改进点梳理清楚。这篇文章会围绕持久化缓存、模块联邦、Tree Shaking 增强、资源模块这几个核心特性逐一展开,并附上可运行的配置示例,帮你判断这次升级到底值不值。

持久化缓存:二次构建速度的根本性提升
Webpack 4 时代的缓存只存在于内存中,进程一退出缓存就没了,第二天上班第一次构建照样要等好几分钟。Webpack 5 引入了文件系统级别的持久化缓存,把编译中间产物直接写到磁盘上,官方称之为 File System Cache。开启方式非常简单,只需在配置中加上两行:
module.exports = {
cache: {
type: 'filesystem',
buildDependencies: {
// 推荐把 webpack 配置文件本身纳入依赖快照
config: [__filename]
}
}
};这样配置之后,第一次冷构建会稍慢一点,因为要额外写入缓存文件到 node_modules/.cache/webpack 目录,但从第二次构建开始,只要源码没有变化,速度可以达到接近秒级。实际项目里,一个中型应用的构建时间从 90 秒降到 8 秒左右是很常见的数字。其原理是 Webpack 5 会为每个模块计算内容哈希,构建时对比磁盘上的快照,只重新编译真正变化过的部分,这种增量策略比 Webpack 4 的硬编码模块路径黑名单要精细得多。
需要特别注意的是 buildDependencies 这个字段。很多团队反馈缓存老是不生效,排查下来多半是因为把 babel 配置、tsconfig 或者环境变量文件排除在快照之外了。任何会影响编译结果的文件都应该列进去,否则可能出现改了配置但产物还是旧的诡异问题。另外,如果你在 CI 环境中使用,建议为缓存目录配置合理的清理策略,避免不同分支的缓存互相污染。
模块联邦:微前端架构的官方解法
模块联邦(Module Federation)可以说是 Webpack 5 里最具架构意义的新特性。它允许多个独立构建的应用在运行时共享模块,一个应用可以动态加载另一个应用暴露出来的组件,且公共依赖只会被加载一次。这对微前端场景来说,解决的是老方案里公共代码重复打包、版本冲突难以协调的顽疾。
先看提供方应用的配置,也就是把组件暴露出去的那一端:
const { ModuleFederationPlugin } = require('webpack').container;
module.exports = {
plugins: [
new ModuleFederationPlugin({
name: 'hostApp',
filename: 'remoteEntry.js',
remotes: {
// 引用另一个应用暴露的模块
utilsApp: 'utilsApp@http://cdn.ipipp.com/remotes/remoteEntry.js'
},
shared: {
// 声明共享依赖,避免 React 被打包两次
react: { singleton: true },
'react-dom': { singleton: true }
}
})
]
};而提供公共组件的远程应用,则通过 exposes 字段把内部模块暴露出去:
new ModuleFederationPlugin({
name: 'utilsApp',
filename: 'remoteEntry.js',
exposes: {
// 外部应用可以通过 utilsApp/Button 引用这个按钮组件
'./Button': './src/components/Button'
},
shared: { react: { singleton: true } }
});shared 配置里的 singleton 选项值得展开讲一下。设置为 true 表示整个运行时只允许存在一个实例,当宿主和远程应用的 React 版本不一致时,会以版本满足范围的那个为准,并打出警告而不是直接崩溃。这在多团队协作、各自控制依赖版本的环境下非常关键。当然模块联邦也不是银弹,它要求所有参与方都使用 Webpack 5,如果你的存量系统还跑在旧版本上,可能需要借助运行时的兼容层,或者干脆继续用 iframe 这类隔离方案过渡。
Tree Shaking 与资源模块的实用改进
除了上面两个大块头,Webpack 5 还有一批容易被忽略但很实用的改进。首先是嵌套的 Tree Shaking,现在可以分析模块内部再导出的结构,把没用到的那部分代码剔除掉。典型场景是入口文件里 re-export 了几十个工具函数,你只用了其中一个,最终产物里其余函数的代码会被清理掉。配合 package.json 中的 sideEffects 字段声明,剔除效果还能进一步扩大。
其次是原生的资源模块支持,新增了 asset、asset/resource、asset/inline、asset/source 四种模块类型,相当于把 file-loader、url-loader、raw-loader 的活儿都收编了。配置示例:
module.exports = {
module: {
rules: [
{
test: /\.png$/,
type: 'asset',
// 小于 8KB 转成 base64 内联,否则输出独立文件
parser: {
dataUrlCondition: { maxSize: 8 * 1024 }
}
}
]
}
};再次是长期缓存稳定性的改进。Webpack 5 用真正的内容哈希替代了原来基于内部结构 ID 的 chunk 哈希算法,这意味着只要文件内容不变,哈希值就不会因为构建顺序或者模块 ID 变化而改变,浏览器缓存命中率会明显提高。此外 Webpack 5 移除了对 Node.js 内置核心模块的自动 polyfill,这个改动是双刃剑:产物体积变小了,但老项目里如果直接在浏览器端代码中 require 了 crypto 或 path 这类模块,构建时会报错,需要手动引入对应的浏览器端 polyfill 或者调整代码逻辑。
从 Webpack 4 迁移时,建议先跑一遍官方提供的升级清单:清理所有指向 webpack 4 内部 API 的自定义插件、确认 node-sass 之类的原生依赖已替换为新版本、再逐项处理 polyfill 报错。迁移完成后,配合持久化缓存和模块联邦,无论是构建效率还是架构灵活性都会有体感明显的提升。至于那些听起来天花乱坠的伪特性名称,直接查官方 changelog 就能辨别,不必让它们影响技术选型的判断。