Webpack 5 发布已经有一段时间,但不少团队在升级时只关注版本号变化,没有真正吃到新特性带来的红利。实际上,Webpack 5 相比 4.x 在构建性能、产物优化和跨应用协作三个方向上都有明显进步。本文挑选几个最有价值的特性逐一分析,包括持久化缓存、模块联邦、更彻底的 Tree Shaking 以及 Asset Modules,配合配置示例说明它们的实际用法。

持久化缓存:让二次构建快一个量级
Webpack 4 时代加速构建的常见手段是 cache-loader 或者硬盘中转缓存插件,但这类方案侵入性强,需要针对每种资源手动配置。Webpack 5 把缓存能力内置到了核心中,通过 cache.type: 'filesystem' 开启基于文件系统的持久化缓存。它会把模块的解析结果、转换后的代码、依赖关系图序列化后写入 node_modules/.cache 目录,下次启动时直接反序列化恢复,跳过大部分重复的 loader 处理和 AST 解析。
配置方式非常简单:
module.exports = {
cache: {
type: 'filesystem',
buildDependencies: {
// 推荐加上 webpack 配置文件本身,配置变更时缓存自动失效
config: [__filename]
}
}
};其中 buildDependencies 很容易被忽略。如果不把配置文件声明为构建依赖,修改 webpack.config.js 后可能仍然命中旧缓存,产出的结果与预期不符,排查起来非常浪费时间。另外,升级 webpack 版本、修改 babel 配置等操作也建议纳入版本控制判断,Webpack 5 内部已经会自动收集一部分依赖,但显式声明更保险。
实测来看,中型项目冷启动 40 秒左右的构建,开启持久化缓存后热启动可以降到 5 秒以内,提升主要来自跳过 loader 转换和模块解析阶段。需要注意的是,CI 环境如果每次都是全新容器,缓存不会生效,可以配合 CI 的缓存目录上传下载功能把 .cache 目录缓存下来,才能真正在流水线上提速。
模块联邦:微前端落地的新选项
模块联邦(Module Federation)是 Webpack 5 最受关注的能力,它允许多个独立构建、独立部署的应用在运行时互相暴露和消费模块。传统微前端方案要么依赖 iframe 隔离带来通信成本,要么需要运行时动态加载脚本自己管理依赖,而模块联邦把共享逻辑下沉到了打包器层面,公共依赖可以配置成单例,避免多个子应用各自打包一份 React 导致页面里出现多份运行时。
提供方和消费方的基本配置如下:
// 应用 A(提供方)
const { ModuleFederationPlugin } = require('webpack').container;
module.exports = {
plugins: [
new ModuleFederationPlugin({
name: 'appA',
filename: 'remoteEntry.js',
exposes: {
'./Button': './src/components/Button'
},
shared: { react: { singleton: true }, 'react-dom': { singleton: true } }
})
]
};
// 应用 B(消费方)
module.exports = {
plugins: [
new ModuleFederationPlugin({
name: 'appB',
remotes: {
appA: 'appA@http://cdn.ipipp.com/appA/remoteEntry.js'
},
shared: { react: { singleton: true }, 'react-dom': { singleton: true } }
})
]
};消费方代码里直接 import Button from 'appA/Button' 即可,Webpack 会在运行时拉取 remoteEntry.js 并解析出真正的 chunk。异步边界是需要注意的点:消费远程模块的入口通常要使用动态 import,因为远程模块的加载天然是异步的。另外 shared 配置里的版本协商依赖 package.json 中的版本号,子应用间依赖版本差距过大时会各自加载一份,失去共享意义,团队内最好约定公共依赖的版本策略。
更彻底的 Tree Shaking 与产物优化
Webpack 5 对 Tree Shaking 做了深度增强,最典型的改进是支持嵌套导出的无用代码消除。比如工具库里存在 export * from './utils' 这种再导出,Webpack 4 往往因为无法精确追踪而保留整棵模块,Webpack 5 则能顺着依赖图分析出真正被使用的命名导出,把没用到的部分剔除掉。对于内部没有副作用的大型工具库,产物体积的缩减比较可观。
同时,新版本对代码生成阶段也做了优化。optimization.realContentHash 默认开启,它基于文件真实内容而非内部哈希来生成文件名中的 hash,只要内容不变,hash 就稳定,有利于 CDN 长期缓存。另外产物中运行时代码默认会拆分成更小的块,按需注入,减少了每个入口重复携带的 boilerplate 体积。
想充分享受 Tree Shaking,库作者和业务方都有责任。库的 package.json 应正确声明 sideEffects: false 或列出有副作用的文件列表,同时提供 ESM 格式的入口。业务方则要避免整体导入的写法,例如用解构或具名导入替代把整个命名空间引入,两者配合才能让摇树真正摇得动。
Asset Modules 与升级注意事项
Webpack 5 内置了资源处理能力,asset/resource、asset/inline、asset/source 和 asset 四种类型分别对应 file-loader、url-loader、raw-loader 以及按大小自动切换的组合能力。这意味着新项目可以不再安装那几个 loader,配置也更统一:
module.exports = {
module: {
rules: [
{
test: /\.(png|jpe?g|gif|svg)$/,
type: 'asset',
parser: {
dataUrlCondition: {
maxSize: 8 * 1024 // 小于 8kb 内联为 base64
}
}
}
]
}
};升级方面还有几个高频踩坑点值得提醒。第一,Webpack 5 不再自动注入 Node.js polyfill,代码里如果直接使用了 process、path 等变量,需要自行引入对应的 polyfill 或者通过 resolve.fallback 配置,否则构建直接报错。第二,部分长期未维护的插件与 Webpack 5 不兼容,升级前建议先梳理插件清单,优先替换为官方维护的新实现。第三,如果项目还在使用 webpack-dev-server 3.x,需要一并升级到 4.x 及以上,配置项有较大调整。
总体来看,持久化缓存解决构建慢的问题,模块联邦解决多应用协作的问题,Tree Shaking 和 Asset Modules 解决产物与配置整洁度的问题。建议按开启缓存、替换资源 loader、验证 Tree Shaking 效果、最后再评估模块联邦的节奏逐步推进,每一步都有可量化的收益验证,升级过程会更稳。