Webpack优化
性能分析
Stats 输出
通过 stats 配置详细输出构建信息:
js
module.exports = {
stats: {
assets: true, // 显示资源信息
modules: true, // 显示模块信息
moduleTrace: true, // 显示模块间的依赖关系
chunks: true, // 显示 chunk 信息
reasons: true, // 显示模块被引入的原因
timings: true, // 显示构建耗时
}
};
使用命令行参数 --json 导出完整构建数据到文件:
shell
webpack --json > stats.json
Performance 选项
设置性能提示阈值,超过时警告或错误:
js
module.exports = {
performance: {
hints: 'warning', // 或 'error'、false
maxAssetSize: 200000, // 单个资源大小限制(字节)
maxEntrypointSize: 400000, // 入口资源大小限制
}
};
webpack-bundle-analyzer 大小分析
使用 webpack-bundle-analyzer
插件可视化分析打包结果:
js
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
module.exports = {
plugins: [
new BundleAnalyzerPlugin({
analyzerMode: 'static', // 生成 HTML 文件
openAnalyzer: false, // 不自动打开
})
]
};
speed-measure-webpack-plugin 速度分析
测量每个 loader 和 plugin 的执行耗时:
js
const SpeedMeasurePlugin = require('speed-measure-webpack-plugin');
const smp = new SpeedMeasurePlugin();
module.exports = smp.wrap({
// 原始 webpack 配置
});
loader性能分析
在 loader 配置中添加 profile: true 记录耗时:
js
{
test: /\.js$/,
use: {
loader: 'babel-loader',
options: {
profile: true
}
}
}
持续集成
每次构建完成,将 stats.json 上传到持续集成服务器
shell
webpack --json > stats.json && aws s3 cp stats.json s3://my-bucket/reports/
速度优化
开启多线程
- 使用
thread-loader
在构建过程中开启多线程:
js
{
test: /\.js$/,
use: {
loader: 'thread-loader',
options: {
workers: 2 // 设置线程数
}
}
}
- happypack(webpack 4 及以下版本)
缓存机制
- webpack 5的持久化缓存(cache 参数)
js
module.exports = {
cache: {
type: 'filesystem', // 使用文件系统缓存
buildDependencies: {
config: [__filename] // 当配置文件变化时重新缓存
}
}
};
- 为loader开启缓存
js
{
test: /\.js$/,
use: {
loader: 'babel-loader',
options: {
cacheDirectory: true // 开启缓存
}
}
}
缩小构建范围
- 精准配置 loader 作用范围,使用 include/exclude 避免处理无关文件:
js
{
test: /\.js$/,
include: path.resolve(__dirname, 'src'), // 只处理 src 目录
exclude: /node_modules/,
use: 'babel-loader'
}
- 优化 resolve 配置
- 减少 resolve.modules 搜索路径
- 使用 resolve.alias 缩短模块查找路径
- 合理配置 resolve.extensions(减少扩展名尝试次数)
js
module.exports = {
resolve: {
modules: [path.resolve(__dirname, 'src'), 'node_modules'],
alias: {
'@': path.resolve(__dirname, 'src')
},
extensions: ['.js', '.ts', '.json'] // 按频率排序
}
};
- noParse,跳过对无需解析的模块(如 jQuery、lodash)的解析:
js
module.exports = {
module: {
noParse: /jquery|lodash/
}
};
开发环境优化
使用
mode: 'development'
开启开发模式,自动启用一些优化选项:- 开发环境友好的代码生成,如不压缩代码,保留注释,保留原始变量名
- 默认开启source-map: devtool: 'eval'
- 开启热更新:devServer:
- 关闭tree-shaking
- 内联css
合适的source-map:使用
devtool: 'eval-source-map'
提高构建速度,便于调试
生产环境优化
- 使用
mode: 'production'
开启生产模式,自动启用代码压缩、tree-shaking 等优化。 - DllPlugin, 预编译第三方库,减少主应用的构建时间:
js
// webpack.dll.js
const webpack = require('webpack');
module.exports = {
entry: {
vendor: ['react', 'react-dom']
},
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')
})
]
};
在主配置中引入 DllReferencePlugin:
js
const webpack = require('webpack');
module.exports = {
plugins: [
new webpack.DllReferencePlugin({
manifest: path.resolve(__dirname, 'dist/dll/vendor-manifest.json')
})
]
};
体积优化
Tree-shaking
- 前提条件:
- 使用 ES6 模块语法(import/export),而非 CommonJS(require)。
- 在 package.json 中添加 "sideEffects": false(或指定无副作用的文件)。
js
// webpack.config.js
module.exports = {
mode: 'production', // 生产模式自动启用 Tree Shaking
optimization: {
usedExports: true, // 标记未使用的导出
minimize: true, // 压缩代码时移除未使用的导出
}
};
代码分割
1. 动态导入 (Dynamic Import)
通过异步加载实现懒加载(如路由级分割):
js
// 同步加载(全部打包到一个文件)
import { add } from './math';
// 异步加载(按需分割成独立 chunk)
button.addEventListener('click', () => {
import('./math').then(math => {
console.log(math.add(1, 2));
});
});
2. SplitChunksPlugin
配置公共模块拆分,避免重复打包:
js
module.exports = {
optimization: {
splitChunks: {
chunks: 'all', // 分割所有类型的 chunk
minSize: 20000, // 最小分割体积(字节)
cacheGroups: {
vendor: { // 分割第三方库
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
chunks: 'all'
},
common: { // 分割公共模块
minChunks: 2,
name: 'common'
}
}
}
}
};
代码压缩
1. JS压缩:terser-webpack-plugin(webpack5自带)
js
module.exports = {
optimization: {
minimizer: [
new TerserPlugin({
terserOptions: {
compress: {
drop_console: true, // 移除 console.log
dead_code: true // 移除死代码
},
mangle: true // 混淆变量名
}
})
]
}
};
2. CSS压缩:css-minimizer-webpack-plugin
js
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
module.exports = {
optimization: {
minimizer: [
new CssMinimizerPlugin()
]
}
};
3. 资源压缩
- 图片压缩:使用 image-minimizer-webpack-plugin:
- 小资源内联:使用 url-loader 或自定义 loader 将小于指定大小的资源内联为 base64:
- 标记移除未使用的CSS:使用 purgecss-webpack-plugin 移除未使用的 CSS:
- 优先引用支持 Tree Shaking 的 ESModule 版本(如 lodash-es 替代 lodash)。
4. CDN 加载外部资源(externals配置)
通过 externals 排除第三方库,改用 CDN 加载,如 jQuery、React 等:
js
// webpack.config.js
module.exports = {
externals: {
react: 'React', // 从全局变量 React 中获取
'react-dom': 'ReactDOM' // 从全局变量 ReactDOM 中获取
}
};
// index.html
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://cdn.jsdelivr.net/npm/react@18.2.0/umd/react.production.min.js"></script>
但有一定的风险,
- 稳定性:CDN加载失败可能性
- 调试成本:与本地开发环境具有差异,缺少source-map
- 安全风险
其他优化角度
- 利用浏览器缓存,使用contenthash 生成文件名,避免浏览器缓存问题。
- 使用 动态导入,按需加载模块,减少初始加载体积。