Webpack4 优化
约 1295 字大约 4 分钟
webpack性能优化打包优化
2026-04-15
构建速度优化
Webpack 打包速度是开发体验的关键。以下从多个维度优化构建速度。
DllPlugin 分离第三方库
使用 DllPlugin 将不常更新的第三方库单独打包,避免每次构建都重新打包。
第一步:创建 dll 配置
// webpack.dll.js
const path = require('path');
const webpack = require('webpack');
module.exports = {
mode: 'production',
entry: {
vendor: ['react', 'react-dom', 'lodash'],
utils: ['axios', 'moment']
},
output: {
filename: '[name].dll.js',
path: path.resolve(__dirname, 'dist/dll'),
library: '[name]_[hash]'
},
plugins: [
new webpack.DllPlugin({
name: '[name]_[hash]',
path: path.resolve(__dirname, 'dist/dll/[name]-manifest.json')
})
]
};第二步:在主配置中引用
// webpack.config.js
const webpack = require('webpack');
const path = require('path');
module.exports = {
// ...其他配置
plugins: [
new webpack.DllReferencePlugin({
manifest: require('./dist/dll/vendor-manifest.json')
}),
new webpack.DllReferencePlugin({
manifest: require('./dist/dll/utils-manifest.json')
})
]
};第三步:在 HTML 中引入 dll 文件
<script src="/dll/vendor.dll.js"></script>
<script src="/dll/utils.dll.js"></script>执行 dll 打包
npx webpack --config webpack.dll.jsresolve 配置优化
合理配置 resolve 可以大幅减少模块解析时间。
module.exports = {
resolve: {
// 优先查找顺序
modules: [
path.resolve(__dirname, 'node_modules'),
'node_modules'
],
// 自动解析的扩展名(减少配置可以提高速度)
extensions: ['.js', '.jsx', '.json'],
// 路径别名
alias: {
'@': path.resolve(__dirname, 'src'),
'@components': path.resolve(__dirname, 'src/components'),
'@utils': path.resolve(__dirname, 'src/utils'),
// 使用轻量级的替代方案
'moment': 'moment/moment.js'
},
// 排除不需要解析的目录
modules: ['node_modules']
}
};缓存优化
hard-source-webpack-plugin(webpack 4)
const HardSourceWebpackPlugin = require('hard-source-webpack-plugin');
module.exports = {
plugins: [
new HardSourceWebpackPlugin({
// 缓存目录
cacheDirectory: 'node_modules/.cache/hard-source/[confighash]',
// 缓存存储在配置
cachePrune: {
maxAge: 2 * 24 * 60 * 60 * 1000, // 2 天
sizeThreshold: 50 * 1024 * 1024 // 50MB
}
})
]
};多线程打包
thread-loader
module.exports = {
module: {
rules: [
{
test: /\.js$/,
use: [
'thread-loader', // 放在最前面
{
loader: 'babel-loader',
options: {
cacheDirectory: true
}
}
],
exclude: /node_modules/
}
]
}
};HappyPack(适用于 webpack 3/4)
const HappyPack = require('happypack');
const os = require('os');
module.exports = {
module: {
rules: [
{
test: /\.js$/,
use: 'happypack/loader?id=babel',
exclude: /node_modules/
}
]
},
plugins: [
new HappyPack({
id: 'babel',
loaders: ['babel-loader?cacheDirectory=true'],
threadPool: HappyPack.ThreadPool({ size: os.cpus().length })
})
]
};noParse 和 Include/Exclude
module.exports = {
module: {
// 不解析大型库的依赖
noParse: /jquery|lodash|moment/,
rules: [
{
test: /\.js$/,
// 只处理 src 目录
include: path.resolve(__dirname, 'src'),
// 排除 node_modules
exclude: /node_modules/,
use: 'babel-loader'
}
]
}
};产物体积优化
代码分割(SplitChunks)
module.exports = {
optimization: {
splitChunks: {
chunks: 'all', // async、initial、all
minSize: 30000, // 最小体积 30KB
maxSize: 0, // 最大体积限制
minChunks: 1, // 最小引用次数
maxAsyncRequests: 5, // 最大异步请求数
maxInitialRequests: 3, // 最大初始请求数
automaticNameDelimiter: '~',
name: true,
cacheGroups: {
// 提取 vendor
vendors: {
test: /[\\/]node_modules[\\/]/,
priority: -10,
name: 'vendors'
},
// 提取公共模块
default: {
minChunks: 2,
priority: -20,
reuseExistingChunk: true
},
// 提取 React 相关
react: {
test: /[\\/]node_modules[\\/](react|react-dom)[\\/]/,
name: 'react',
priority: 20
}
}
},
// 提取 runtime
runtimeChunk: 'single'
}
};Tree Shaking
// package.json
{
"sideEffects": [
"*.css",
"*.scss",
"@babel/polyfill"
]
}// webpack.prod.js
module.exports = {
optimization: {
usedExports: true, // 标记未使用的导出
minimize: true, // 启用压缩
minimizer: [
new TerserPlugin({
terserOptions: {
compress: {
unused: true, // 移除未使用的代码
dead_code: true, // 移除死代码
drop_console: true, // 移除 console
drop_debugger: true
}
}
})
]
}
};压缩优化
const TerserPlugin = require('terser-webpack-plugin');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
module.exports = {
optimization: {
minimizer: [
// JS 压缩
new TerserPlugin({
parallel: true, // 多进程
sourceMap: true,
terserOptions: {
compress: {
warnings: false,
drop_console: true,
drop_debugger: true,
pure_funcs: ['console.log']
}
}
}),
// CSS 压缩
new OptimizeCSSAssetsPlugin({
assetNameRegExp: /\.css\.*(?!.*map)/g,
cssProcessor: require('cssnano'),
cssProcessorPluginOptions: {
preset: ['default', { discardComments: { removeAll: true } }]
}
})
]
}
};开发体验优化
模块热替换(HMR)原理
HMR 允许在运行时替换、添加、删除模块,而无需重新加载页面。
工作流程
- 文件修改被监听
- Webpack 重新编译生成新的 chunk
- 通过 WebSocket 通知浏览器
- 浏览器请求新的模块
- 运行时替换旧模块
配置 HMR
const webpack = require('webpack');
module.exports = {
devServer: {
hot: true,
open: true,
port: 3000
},
plugins: [
new webpack.HotModuleReplacementPlugin()
]
};在代码中接受热更新
if (module.hot) {
module.hot.accept('./module.js', () => {
console.log('模块已更新');
// 重新执行逻辑
});
}可视化工具
webpack-bundle-analyzer
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
module.exports = {
plugins: [
new BundleAnalyzerPlugin({
analyzerMode: 'server',
analyzerHost: '127.0.0.1',
analyzerPort: 8888,
openAnalyzer: true
})
]
};speed-measure-webpack-plugin
const SpeedMeasurePlugin = require('speed-measure-webpack-plugin');
const smp = new SpeedMeasurePlugin();
const config = {
// webpack 配置
};
module.exports = smp.wrap(config);生产环境完整配置示例
// webpack.prod.js
const path = require('path');
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const TerserPlugin = require('terser-webpack-plugin');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
module.exports = {
mode: 'production',
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'js/[name].[chunkhash:8].js',
chunkFilename: 'js/[name].[chunkhash:8].chunk.js',
publicPath: '/'
},
module: {
rules: [
{
test: /\.(js|jsx)$/,
include: path.resolve(__dirname, 'src'),
exclude: /node_modules/,
use: [
'thread-loader',
{
loader: 'babel-loader',
options: {
cacheDirectory: true
}
}
]
},
{
test: /\.css$/,
use: [
MiniCssExtractPlugin.loader,
{
loader: 'css-loader',
options: {
importLoaders: 1
}
},
'postcss-loader'
]
}
]
},
resolve: {
extensions: ['.js', '.jsx', '.json'],
alias: {
'@': path.resolve(__dirname, 'src')
}
},
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
template: './src/index.html',
minify: {
removeComments: true,
collapseWhitespace: true
}
}),
new MiniCssExtractPlugin({
filename: 'css/[name].[contenthash:8].css'
}),
new webpack.HashedModuleIdsPlugin()
],
optimization: {
minimize: true,
minimizer: [
new TerserPlugin({
parallel: true,
terserOptions: {
compress: {
drop_console: true,
drop_debugger: true
}
}
}),
new OptimizeCSSAssetsPlugin()
],
splitChunks: {
chunks: 'all',
cacheGroups: {
vendors: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
priority: -10
},
common: {
minChunks: 2,
priority: -20,
reuseExistingChunk: true
}
}
},
runtimeChunk: 'single'
},
devtool: 'source-map',
performance: {
hints: 'warning',
maxAssetSize: 300000,
maxEntrypointSize: 500000
}
};
// 需要分析时取消注释
// if (process.env.ANALYZE) {
// module.exports.plugins.push(new BundleAnalyzerPlugin());
// }优化前后对比
| 优化项 | 优化前 | 优化后 | 提升 |
|---|---|---|---|
| 首次构建时间 | 45s | 15s | 66% ↓ |
| 二次构建时间 | 40s | 8s | 80% ↓ |
| 打包体积 | 1.2MB | 450KB | 62% ↓ |
| 页面加载时间 | 3.2s | 1.1s | 65% ↓ |
最佳实践
- 开发环境:优先使用 HMR、cache、noParse
- 生产环境:代码分割、压缩、Tree Shaking
- 大型项目:使用 DllPlugin、多线程、缓存
- 监控分析:定期使用 BundleAnalyzer 分析体积
- 持续优化:根据 speed-measure 定位慢的地方
