2025年11月20日6 分钟
Webpack 5 构建提速方案:从分钟级到秒级
系统性梳理 Webpack 5 构建速度优化的 10+ 种方案,涵盖缓存、并行、缩小范围、DLL 等核心策略
为什么 Webpack 构建慢
Webpack 构建的本质是:遍历所有模块 → 对每个模块执行 Loader 转换 → 生成依赖图 → 打包输出。慢的原因主要有三个:
1. 文件太多:几千个模块需要逐一处理
2. 转换太慢:Babel/TypeScript 编译是 CPU 密集型操作
3. 重复工作:每次构建都从头开始,不复用上次结果
一、持久化缓存(最大提速)
Webpack 5 最重要的新特性——把构建结果缓存到磁盘,第二次构建直接复用。
// webpack.config.js
module.exports = {
cache: {
type: 'filesystem', // 关键:开启磁盘缓存
buildDependencies: {
config: [__filename], // 配置文件变化时使缓存失效
},
cacheDirectory: path.resolve(__dirname, '.webpack_cache'),
version: '1.0', // 手动版本号,改变时清空缓存
},
}
效果对比:
首次构建: 45s
第二次(缓存命中): 3s ← 提速 15 倍 🚀
修改一个文件后: 5s ← 只重新编译变化的模块
缓存失效策略
cache: {
type: 'filesystem',
// 当这些文件变化时,缓存完全失效
buildDependencies: {
config: [__filename], // webpack 配置文件
tsconfig: [path.resolve(__dirname, 'tsconfig.json')],
},
// 当这些值变化时,缓存失效
version: createHash('md4')
.update(process.env.NODE_ENV)
.update(JSON.stringify(process.env))
.digest('hex'),
}
二、缩小构建范围
1. 精确 include/exclude
module: {
rules: [
{
test: /\.(ts|tsx)$/,
// ✅ 只处理 src 目录
include: path.resolve(__dirname, 'src'),
// ✅ 排除 node_modules(它们已经编译过了)
exclude: /node_modules/,
use: 'swc-loader',
},
],
},
2. resolve 优化
resolve: {
// ✅ 减少后缀尝试次数(按频率排序)
extensions: ['.tsx', '.ts', '.js'], // 不要写 .json .css 等
// ✅ 指定第三方包入口
mainFields: ['browser', 'module', 'main'],
// ✅ 路径别名避免深层遍历
alias: {
'@': path.resolve(__dirname, 'src'),
},
// ✅ 只在 src 和 node_modules 中查找模块
modules: [path.resolve(__dirname, 'src'), 'node_modules'],
},
3. 跳过不需要解析的模块
module: {
// 这些库没有依赖其他模块,不需要解析
noParse: /jquery|lodash|moment/,
},
三、并行处理
1. thread-loader(多线程 Loader)
module: {
rules: [
{
test: /\.(ts|tsx)$/,
use: [
{
loader: 'thread-loader',
options: {
workers: require('os').cpus().length - 1, // CPU 核心数 - 1
workerParallelJobs: 50,
poolTimeout: 2000,
},
},
'babel-loader',
],
},
],
},
2. TerserPlugin 并行压缩
const TerserPlugin = require('terser-webpack-plugin')
optimization: {
minimizer: [
new TerserPlugin({
parallel: true, // 多进程并行压缩
terserOptions: {
compress: {
drop_console: true, // 移除 console.log
},
},
}),
],
},
四、使用更快的工具替代
1. SWC 替代 Babel(快 20-70 倍)
module: {
rules: [
{
test: /\.(ts|tsx)$/,
// ❌ babel-loader: 单文件编译 50ms
// ✅ swc-loader: 单文件编译 2ms
use: {
loader: 'swc-loader',
options: {
jsc: {
parser: { syntax: 'typescript', tsx: true },
transform: { react: { runtime: 'automatic' } },
},
},
},
},
],
},
2. esbuild-loader 替代 Babel + Terser
const { EsbuildPlugin } = require('esbuild-loader')
module: {
rules: [{
test: /\.(ts|tsx)$/,
loader: 'esbuild-loader',
options: { target: 'es2020' },
}],
},
optimization: {
minimizer: [
new EsbuildPlugin({ target: 'es2020' }), // esbuild 压缩比 Terser 快 100 倍
],
},
五、DLL 预编译(大型项目)
把不常变动的第三方库预先编译,避免每次都重新打包:
// webpack.dll.config.js — 只运行一次
module.exports = {
entry: {
vendor: ['react', 'react-dom', 'lodash'],
},
output: {
path: path.resolve(__dirname, 'dll'),
filename: '[name].dll.js',
library: '[name]_dll',
},
plugins: [
new webpack.DllPlugin({
name: '[name]_dll',
path: path.resolve(__dirname, 'dll/[name].manifest.json'),
}),
],
}
// webpack.config.js — 正常构建引用 DLL
plugins: [
new webpack.DllReferencePlugin({
manifest: require('./dll/vendor.manifest.json'),
}),
],
六、代码分割优化
optimization: {
splitChunks: {
chunks: 'all',
cacheGroups: {
// 第三方库单独打包(变动少,缓存命中率高)
vendors: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
priority: 10,
chunks: 'all',
},
// 被 2 个以上页面共享的模块
commons: {
minChunks: 2,
name: 'commons',
priority: 5,
reuseExistingChunk: true,
},
},
},
},
七、开发模式专项优化
// 开发模式配置
module.exports = {
mode: 'development',
// ✅ eval 速度最快(不需要 Source Map 精确到列)
devtool: 'eval-cheap-module-source-map',
// ✅ 开发时不压缩
optimization: {
minimize: false,
removeAvailableModules: false,
removeEmptyChunks: false,
splitChunks: false,
},
// ✅ 增量编译
watchOptions: {
ignored: /node_modules/, // 不监听 node_modules
aggregateTimeout: 300, // 300ms 内的变更合并处理
},
// ✅ 只输出变化的模块
output: {
pathinfo: false, // 减少生成代码的信息注释
},
}
八、构建分析
# 速度分析
npm install speed-measure-webpack-plugin -D
# 体积分析
npm install webpack-bundle-analyzer -D
const SpeedMeasurePlugin = require('speed-measure-webpack-plugin')
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
const smp = new SpeedMeasurePlugin()
module.exports = smp.wrap({
plugins: [
new BundleAnalyzerPlugin({
analyzerMode: 'static',
reportFilename: 'bundle-report.html',
}),
],
})
Speed Measure Plugin 输出:
SWC Loader: 2.3s
CSS Loader: 1.1s
TerserPlugin: 5.2s
Total: 12.8s
→ TerserPlugin 最慢 → 替换为 esbuild-loader → 降到 0.5s
优化清单总结
| 方案 | 提速效果 | 适用阶段 | 复杂度 |
|---|---|---|---|
| filesystem cache | ⭐⭐⭐⭐⭐ | 开发+生产 | 低 |
| SWC/esbuild 替代 Babel | ⭐⭐⭐⭐ | 开发+生产 | 低 |
| 缩小 include/exclude | ⭐⭐⭐ | 开发+生产 | 低 |
| thread-loader 并行 | ⭐⭐⭐ | 生产 | 中 |
| resolve 优化 | ⭐⭐ | 开发+生产 | 低 |
| DLL 预编译 | ⭐⭐⭐ | 开发 | 中 |
| devtool 选择 | ⭐⭐ | 开发 | 低 |
| esbuild 压缩 | ⭐⭐⭐⭐ | 生产 | 低 |
优先做前三项,就能覆盖 80% 的提速效果。