Webpack 5 的产品创新并不是简单地在 4.x 基础上叠加补丁,而是从构建机制层面引入了模块联邦、持久化缓存和资源模块三个关键能力。这些特性分别对应大型组织的代码共享、多次构建的速度瓶颈以及资源管线的简化需求。理解它们的设计动机,比记住配置项更重要。
一、模块联邦:从构建时共享到运行时编排
模块联邦是 Webpack 5 中最具产品创新色彩的特性。在传统微前端方案里,公共依赖要么被打进每一个子应用造成体积膨胀,要么通过 externals 挂载到全局变量,一旦版本升级就容易出现运行时冲突。模块联邦改变这个思路,它允许应用在运行时动态加载其他应用暴露出来的模块,而无需在构建阶段知道对方的存在。
具体来说,每个应用都可以通过 ModuleFederationPlugin 声明自己向外暴露的模块,以及需要消费的远程模块。构建产物中会生成 remoteEntry.js 作为远程入口,主应用在浏览器加载这个文件后,就能拿到远程应用导出的组件或工具函数。远程模块共享同一份依赖实例,因此 React、Vue 这类框架不会被重复打包。下面是一个两个应用之间共享组件的简单配置示例:
// 主应用 webpack.config.js
const ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin');
module.exports = {
plugins: [
new ModuleFederationPlugin({
name: 'host',
remotes: {
app2: 'app2@http://localhost:3002/remoteEntry.js'
},
shared: {
react: { singleton: true },
'react-dom': { singleton: true }
}
})
]
};
远程应用对应配置里的 name 和 exposes,可以暴露一个按钮组件或者整个页面容器。由于模块联邦是基于 webpack 的模块运行时,它天然支持代码分割和按需加载。共享依赖通过 singleton 选项强制全局只保留一份实例,避免 React 等状态库出现多实例错误。这个能力让大型组织能够以产品线为单位独立迭代,同时保持统一的运行环境。
二、持久化缓存:把二次构建时间压到最低
Webpack 5 之前,缓存主要依赖内存和外部工具,一旦进程结束,下次构建就要重新解析所有模块。对于几百个页面的大型工程,冷启动可能耗时数分钟。Webpack 5 内置的持久化缓存可以直接把中间结果写入磁盘,大幅缩短二次构建、热更新和 watch 模式的响应时间。
配置启用非常简单,只需要在 cache 字段指定 filesystem 类型。默认情况下,缓存数据会保存在 node_modules/.cache/webpack 目录。开发者还可以通过 buildDependencies 把配置文件本身纳入缓存失效范围,避免修改配置后误用旧的缓存。示例如下:
module.exports = {
cache: {
type: 'filesystem',
buildDependencies: {
config: [__filename]
}
}
};
持久化缓存并不是无脑缓存所有内容。Webpack 会追踪模块依赖、loader 结果和 chunk 信息,只有文件内容或相关配置发生变化时,对应缓存才会失效。从实际项目观察来看,二次构建通常能比冷启动快百分之五十以上,复杂项目甚至可以从分钟级降到秒级。配合模块联邦使用时,远程应用的独立缓存还能进一步降低 CI 环境的重复安装成本。
三、资源模块:告别 loader 链路的统一处理
Webpack 4 时期处理图片、字体和音频需要分别安装 file-loader、url-loader 和 raw-loader,并在 rules 里写大量 chain 配置。Webpack 5 将这些能力收敛为四种资源模块类型:asset/resource、asset/inline、asset/source 和 asset。其中 asset 类型会根据文件大小自动在导出 URL 和 base64 内联之间切换,行为类似以前的 url-loader 加 limit 参数。
使用资源模块后,配置不再依赖外部 loader 包,只通过 type 字段声明处理方式。例如小于 8KB 的图片自动内联,超过阈值则单独输出文件:
module.exports = {
module: {
rules: [
{
test: /\.(png|jpg|gif)$/i,
type: 'asset',
parser: {
dataUrlCondition: {
maxSize: 8 * 1024
}
}
},
{
test: /\.(woff2?|eot|ttf|otf)$/i,
type: 'asset/resource'
}
]
}
};
资源模块内部会生成正确的引用地址,并支持通过 output.assetModuleFilename 自定义输出目录结构。相比旧 loader 方案,这套内置机制减少依赖安装数量,也避免了不同 loader 之间版本冲突。对于需要直接读取文件内容的场景,asset/source 类型可以把文件作为字符串导入,这在模板文件和 Web Worker 资源处理中非常实用。产品创新在这里体现为:把分散的社区维护能力吸收进核心,让基础资源处理变成开箱即用的标准功能。
四、Tree Shaking 与 Top Level Await:隐藏在细节里的体验提升
Webpack 5 对 Tree Shaking 的改进同样属于产品创新范畴。它从单纯的 ES 模块依赖图优化,延伸到对副作用声明的精细控制。通过 package.json 的 sideEffects 字段,开发者可以告诉 webpack 哪些文件是安全可删除的。对于使用 ESM 的库,未引用的导出不会再被打包,产物体积因此更小。
另一个容易忽视的特性是 Top Level Await。启用 experiments.topLevelAwait 后,入口模块的顶层可以直接使用 await 表达式,无需包一层 async 函数。这在初始化脚本、动态加载配置等场景里能显著提升代码可读性。配置方法如下:
module.exports = {
experiments: {
topLevelAwait: true
}
};
这两个改进并不像模块联邦那样高调,但它们共同影响最终用户的产品体验。更小的打包体积意味着更快的首屏加载,更简洁的异步代码降低了出错概率。将 Webpack 5 的产品创新看作一套组合拳,而不是单个特性,才能真正发挥其工程价值。