Webpack 性能优化实战指南
作为前端工程师,我在日常开发中经常使用 Webpack 进行项目构建和打包。随着项目规模的不断增长,Webpack 的构建速度和输出包的大小逐渐成为影响开发效率和用户体验的瓶颈。为了解决这些问题,我总结了一些在实际项目中可以应用的 Webpack 性能优化措施,主要分为两大类:提高构建速度和减小输出包大小。
提高构建速度的优化措施
升级 Webpack 和 Node.js 版本
使用最新的 Webpack 5.x 和 Node.js 14.x 及以上版本,可以显著提升构建性能。新版本通常会包含一些性能优化和新特性,能够加快构建速度。
优化 Loader 配置
合理配置 Loader 可以减少不必要的文件处理,提高构建效率:
module.exports = {
module: {
rules: [
{
test: /\.js$/,
include: path.resolve(__dirname, 'src'), // 只处理 src 目录下的文件
exclude: /node_modules/, // 排除 node_modules 目录
use: ['babel-loader?cacheDirectory'], // 使用缓存加速二次构建
},
],
},
};
优化 resolve 配置
调整 resolve 配置可以减少模块搜索的范围和解析的次数:
module.exports = {
resolve: {
modules: [path.resolve(__dirname, 'src'), 'node_modules'], // 限定模块搜索目录
extensions: ['.js', '.json'], // 限定扩展名解析顺序
alias: {
'@': path.resolve(__dirname, 'src'), // 设置路径别名
},
},
};
使用 DllPlugin 预编译框架/库
将不常变动的第三方框架/库(如 React、Vue)预先编译为动态链接库(dll),可以大幅度减少二次编译的时间。
// webpack.dll.config.js
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', 'manifest.json'),
}),
],
};
// webpack.config.js
module.exports = {
plugins: [
new webpack.DllReferencePlugin({
manifest: path.resolve(__dirname, 'dist', 'dll', 'manifest.json'),
}),
],
};
并行化处理 Loader
使用 HappyPack 或 thread-loader 可以将 Loader 的执行过程多进程/多实例化,充分利用多核 CPU 加速构建。
// 使用 HappyPack
const HappyPack = require('happypack');
module.exports = {
module: {
rules: [
{
test: /\.js$/,
use: 'happypack/loader?id=babel',
},
],
},
plugins: [
new HappyPack({
id: 'babel',
loaders: ['babel-loader?cacheDirectory'],
}),
],
};
// 使用 thread-loader
module.exports = {
module: {
rules: [
{
test: /\.js$/,
use: ['thread-loader', 'babel-loader?cacheDirectory'],
},
],
},
};
选择合适的 devtool
不同的 devtool 设置会影响构建和重新构建的速度,一般来说: eval 和 eval-source-map 模式构建速度最快,适合开发环境。 cheap-source-map 相对较快,适合生产环境。 source-map 构建速度慢,但是调试最友好。
控制 SourceMap 的生成
如果项目不需要使用 SourceMap,可以关闭它的生成,从而加速构建。
module.exports = {
devtool: false,
};
使用 IgnorePlugin 忽略无用文件
使用 IgnorePlugin 可以忽略某些不需要被打包的文件,从而减小输出包的体积。
module.exports = {
plugins: [
new webpack.IgnorePlugin({
resourceRegExp: /^\.\/locale$/,
contextRegExp: /moment$/,
}),
],
};
分析构建性能瓶颈
使用 Webpack 内置的 profile 工具可以生成性能分析报告,结合 webpack-bundle-analyzer 等可视化分析工具,找出构建性能的瓶颈,有针对性地进行优化。
webpack --profile --json > stats.json
减小输出包大小的优化措施
启用 Tree Shaking
通过设置 optimization.usedExports 为 true,Webpack 会自动删除没有被使用的代码,减小输出包的体积。
module.exports = {
optimization: {
usedExports: true,
},
};
拆分代码
使用动态导入语法 import() 和 splitChunks 配置,可以将代码拆分为多个异步加载的 chunk,避免加载用不到的代码。
// 动态导入
import('./module').then((module) => {
// ...
});
// webpack.config.js
module.exports = {
optimization: {
splitChunks: {
chunks: 'all',
},
},
};
压缩代码
使用 TerserPlugin 压缩 JavaScript 代码,使用 MiniCssExtractPlugin 和 OptimizeCSSAssetsPlugin 压缩 CSS 代码。
module.exports = {
optimization: {
minimize: true,
minimizer: [new TerserPlugin(), new OptimizeCSSAssetsPlugin()],
},
plugins: [new MiniCssExtractPlugin()],
};
开启 Scope Hoisting
使用 ModuleConcatenationPlugin 插件开启 Scope Hoisting 可以让 Webpack 打包出来的代码文件更小、运行的更快。
module.exports = {
plugins: [new webpack.optimize.ModuleConcatenationPlugin()],
};
提取 Runtime 代码
将 optimization.runtimeChunk 设置为 true 或 'multiple',可以将 chunk 之间共享的运行时代码提取到单独的 chunk 中,减小 entry chunk 体积。
module.exports = {
optimization: {
runtimeChunk: true,
},
};
使用 externals 配置
对于一些大型的第三方库(如 jQuery、Lodash),可以使用 externals 配置将其从 bundle 中排除,而使用 CDN 等外部资源引入,从而减小打包体积。
module.exports = {
externals: {
jquery: 'jQuery',
},
};
优化图片和字体文件
使用 image-webpack-loader 对图片进行压缩,使用 url-loader 将小图片转换为 base64 内联。对于字体文件,也可以使用 url-loader 处理。
module.exports = {
module: {
rules: [
{
test: /\.(png|jpe?g|gif|svg)$/,
use: [
{
loader: 'url-loader',
options: {
limit: 8192,
},
},
{
loader: 'image-webpack-loader',
options: {
mozjpeg: {
progressive: true,
quality: 65,
},
},
},
],
},
{
test: /\.(woff2?|eot|ttf|otf)$/,
use: [
{
loader: 'url-loader',
options: {
limit: 8192,
},
},
],
},
],
},
};
去除无用的 CSS
使用 PurgeCSS 插件可以自动检测并删除没有被使用的 CSS 代码,大大减小 CSS 文件的体积。
const PurgecssPlugin = require('purgecss-webpack-plugin');
const glob = require('glob');
module.exports = {
plugins: [
new PurgecssPlugin({
paths: glob.sync(`${path.resolve(__dirname, 'src')}/**/*`, { nodir: true }),
}),
],
};