每次项目迭代,构建时间从几十秒涨到几分钟,却说不清到底慢在哪个环节——这是不少团队切换到 Webpack 5 之后最想解决的问题。Webpack 5 在性能方面做了大量工作,除了持久化缓存这类直接的提速手段,还提供了一套相对完整的性能监控能力(Performance Monitoring 相关特性),包括编译耗时统计、资源体积提示、多编译过程的 stats 输出等。这篇文章就围绕这些能力展开,说明它们分别解决什么问题、怎么配置、以及如何借助监控数据反过来优化构建流程。

一、性能监控体系的两个入口:stats 与 performance
Webpack 的性能监控并不是一个独立的插件,而是分散在 stats 和 performance 两块配置里。stats 负责输出构建过程的统计信息,比如各模块的构建耗时、子编译的耗时分布;performance 则关注产物本身,当资源体积超过阈值时给出警告或错误提示。理解这两者的分工,是做性能分析的第一步。
先看 stats 中与耗时相关的字段。默认情况下命令行输出比较简略,只有总耗时,想要看细节需要显式开启:
// webpack.config.js
module.exports = {
stats: {
all: false,
timing: true, // 输出构建总耗时与各阶段耗时
modules: true, // 输出模块列表
moduleTrace: true, // 追踪模块依赖链路
builtAt: true, // 构建时间戳
errors: true,
warnings: true,
children: true // 包含子编译(如 worker、dll)的统计
}
};
开启 timing 之后,输出里会出现每个阶段的耗时数据。对多入口或者使用了 child compilation 的项目(比如 mini-css-extract-plugin、worker 打包),children 选项尤其重要,否则子编译的耗时会被折叠掉,你会误以为主编译很快。另外一个容易被忽略的细节是 logging 字段,把它设置为 'verbose' 可以看到 Webpack 内部插件(如 SplitChunksPlugin)的日志时间线,对定位是哪个插件拖慢了构建很有帮助。
再看 performance 配置。它监控的是产物体积:
module.exports = {
performance: {
hints: 'warning', // 'warning' | 'error' | false
maxAssetSize: 300 * 1024, // 单个资源上限 300KB
maxEntrypointAssetSize: 500 * 1024, // 入口资源上限 500KB
assetFilter: function(assetFilename) {
// 排除 sourcemap 文件
return !/\.map$/.test(assetFilename);
}
}
};
hints 设为 'error' 时,超限资源会直接导致构建失败,这个策略在 CI 环境里特别有用——可以把体积预算固化到流水线中,防止某个同事随手引入一个大依赖导致线上体积失控。assetFilter 则让你可以排除掉不需要监控的文件,典型场景是地图文件和字体文件。需要注意的是,这里的体积是解析后产物的大小,与浏览器实际下载的 gzip 体积有差异,如果对体积预算要求精确,建议在监控层面换算。
二、量化构建耗时:从日志到 Node API
靠控制台日志肉眼观察耗时,适合临时排查,但要做长期监控和趋势对比,就得借助 Node API。Webpack 5 对 MultiStats 和 Stats 对象做了整理,暴露出结构化的 JSON 数据,包含 time、builtAt、每个模块的构建信息等,这为自定义监控提供了基础。
一个典型做法是用 Node 脚本驱动构建并采集数据:
const webpack = require('webpack');
const config = require('./webpack.config.js');
const compiler = webpack(config);
const startTime = Date.now();
compiler.run((err, stats) => {
if (err) {
console.error(err);
process.exit(1);
}
const json = stats.toJson({
all: false,
timing: true,
modules: true,
children: true
});
// 总耗时(毫秒)
console.log('总耗时:', json.time);
// 找出体积最大的模块
const modules = json.children
? json.children.flatMap(c => c.modules || [])
: (json.modules || []);
const top = modules
.sort((a, b) => b.size - a.size)
.slice(0, 10);
top.forEach(m => console.log(m.size, m.name));
compiler.close(() => console.log('构建结束'));
});
这段脚本做了三件事:拿到结构化的耗时数据、展开子编译的模块列表、按体积排序输出 Top 10 模块。把它接到 CI 流水线里,每次构建把 json.time 写入时序数据库,几周之后你就能看到构建时间的完整曲线,任何一次异常上涨都能对应到具体的提交记录。相比拍脑袋说「感觉变慢了」,数据曲线的说服力要强得多。
如果需要更细粒度的阶段耗时,Webpack 5 还支持通过 compiler.hooks 打点。比如想知道从读取配置到模块编译完成之间花了多久,可以监听 compilation 钩子并记录时间差。社区里的 speed-measure-webpack-plugin 本质上也是这个思路,但要注意它对部分 Webpack 5 插件的兼容性并不完美,遇到报错时可以退回手动打点的方式。
三、监控与优化手段的配合:让数据驱动决策
监控本身不产生性能收益,它的价值在于指导优化。Webpack 5 的持久化缓存(cache: { type: 'filesystem' })是最大的提速利器,但缓存命中的效果需要监控数据来验证。一个实践建议是:在开启缓存前后各跑一周的构建监控,对比 json.time 的分布,你会得到一个可信的提速比例,而不是缓存命中时飞快、未命中时依旧缓慢的模糊印象。
几条与监控直接相关的优化经验值得分享。第一,如果监控数据显示大量时间花在 SplitChunksPlugin 或压缩阶段,优先考虑用 esbuild-loader 或开启多线程压缩来针对性处理,而不是盲目升级硬件。第二,如果 Top 10 模块列表里出现了意料之外的大依赖(比如只用了两个函数却引入了整个 lodash),说明依赖治理比构建优化更紧迫,按需引入或替换为模块化程度更高的库收益更明显。第三,performance.hints 报出的入口体积超限,通常要结合 optimization.splitChunks 的拆分策略来处理,把公共依赖抽到独立 chunk,利用浏览器缓存。
最后补充一点工程化层面的建议:性能监控要形成闭环才有意义。采集数据、输出报表、在体积或耗时超阈值时阻断流水线,这三步串起来之后,构建性能就从一个「玄学问题」变成了可管理的技术指标。团队规模越大,这套闭环的价值越明显——新成员提交的代码如果让构建时间暴涨 30%,监控系统会在合并之前就发出警告,而不是等到某天所有人都开始抱怨构建太慢才回头排查。
Webpack 5性能监控Performance Monitoring修改时间:2026-09-08 11:27:59