Webpack 5 生产环境打包优化
Webpack 5 提供了许多优化生产环境打包的方法。以下是一些关键的优化策略和详细代码实现:
1. 分离配置文件
首先,建议将开发环境和生产环境的配置分开:
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: {
app: './src/index.js',
},
output: {
filename: '[name].[contenthash].js',
path: path.resolve(__dirname, 'dist'),
clean: true,
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html',
}),
],
};
const { merge } = require('webpack-merge');
const common = require('./webpack.common.js');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
const TerserPlugin = require('terser-webpack-plugin');
module.exports = merge(common, {
mode: 'production',
devtool: 'source-map',
module: {
rules: [
{
test: /\.css$/,
use: [MiniCssExtractPlugin.loader, 'css-loader'],
},
],
},
plugins: [
new MiniCssExtractPlugin({
filename: '[name].[contenthash].css',
}),
],
optimization: {
minimizer: [
new TerserPlugin(),
new CssMinimizerPlugin(),
],
},
});
2. 代码分割 (Code Splitting)
const { merge } = require('webpack-merge');
const common = require('./webpack.common.js');
// ... 其他引入
module.exports = merge(common, {
mode: 'production',
// ... 其他配置
optimization: {
runtimeChunk: 'single',
splitChunks: {
chunks: 'all',
maxInitialRequests: Infinity,
minSize: 20000,
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
name(module) {
// 获取第三方库的名称
const packageName = module.context.match(/[\\/]node_modules[\\/](.*?)([\\/]|$)/)[1];
return `npm.${packageName.replace('@', '')}`;
},
},
},
},
minimizer: [
new TerserPlugin(),
new CssMinimizerPlugin(),
],
},
});
3. 压缩 CSS 和 JS
const { merge } = require('webpack-merge');
const common = require('./webpack.common.js');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
const TerserPlugin = require('terser-webpack-plugin');
module.exports = merge(common, {
mode: 'production',
// ... 其他配置
optimization: {
minimize: true,
minimizer: [
new TerserPlugin({
terserOptions: {
compress: {
drop_console: true, // 移除 console
},
format: {
comments: false, // 移除注释
},
},
extractComments: false,
}),
new CssMinimizerPlugin(),
],
},
});
4. 使用 Tree Shaking
Webpack 5 默认在生产模式下启用 Tree Shaking,但你可以进一步优化:
{
"name": "your-project",
"sideEffects": [
"*.css",
"*.scss"
]
}
5. 资源模块优化
// ... 其他引入
module.exports = merge(common, {
// ... 其他配置
module: {
rules: [
{
test: /\.(png|svg|jpg|jpeg|gif)$/i,
type: 'asset',
parser: {
dataUrlCondition: {
maxSize: 8 * 1024 // 8kb 以下转为 inline 资源
}
},
generator: {
filename: 'images/[name].[hash][ext]'
}
},
{
test: /\.(woff|woff2|eot|ttf|otf)$/i,
type: 'asset/resource',
generator: {
filename: 'fonts/[name].[hash][ext]'
}
}
]
}
});
6. 使用缓存
module.exports = {
// ... 其他配置
output: {
filename: '[name].[contenthash].js',
path: path.resolve(__dirname, 'dist'),
clean: true,
},
// ... 其他配置
};
7. 使用 Compression Plugin
const CompressionPlugin = require('compression-webpack-plugin');
module.exports = merge(common, {
// ... 其他配置
plugins: [
// ... 其他插件
new CompressionPlugin({
algorithm: 'gzip',
test: /\.(js|css|html|svg)$/,
threshold: 10240, // 只有大于 10kb 的资源才会被处理
minRatio: 0.8 // 只有压缩率小于这个值的资源才会被处理
}),
],
});
8. 使用 Bundle Analyzer
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');
module.exports = merge(common, {
// ... 其他配置
plugins: [
// ... 其他插件
new BundleAnalyzerPlugin({
analyzerMode: 'static',
openAnalyzer: false,
reportFilename: 'bundle-report.html'
}),
],
});
9. 优化图片
const ImageMinimizerPlugin = require('image-minimizer-webpack-plugin');
module.exports = merge(common, {
// ... 其他配置
optimization: {
// ... 其他优化
minimizer: [
// ... 其他 minimizer
new ImageMinimizerPlugin({
minimizer: {
implementation: ImageMinimizerPlugin.imageminMinify,
options: {
plugins: [
['gifsicle', { interlaced: true }],
['jpegtran', { progressive: true }],
['optipng', { optimizationLevel: 5 }],
['svgo', { plugins: [{ removeViewBox: false }] }],
],
},
},
}),
],
},
});
Webpack 5 生产环境打包优化(续)
10. 使用 Module Federation (微前端)
Module Federation 是 Webpack 5 的一个重要新特性,允许多个独立的构建共享代码:
const { ModuleFederationPlugin } = require('webpack').container;
module.exports = merge(common, {
// ... 其他配置
plugins: [
// ... 其他插件
new ModuleFederationPlugin({
name: 'app1',
filename: 'remoteEntry.js',
exposes: {
'./Component': './src/Component',
},
shared: {
react: { singleton: true },
'react-dom': { singleton: true },
},
}),
],
});
11. 预加载和预获取
通过在 HTML 中添加 <link rel="preload"> 或 <link rel="prefetch"> 标签来优化资源加载:
const PreloadWebpackPlugin = require('@vue/preload-webpack-plugin');
module.exports = merge(common, {
// ... 其他配置
plugins: [
// ... 其他插件
new PreloadWebpackPlugin({
rel: 'preload',
include: 'initial',
fileBlacklist: [/\.map$/, /hot-update\.js$/],
}),
new PreloadWebpackPlugin({
rel: 'prefetch',
include: 'asyncChunks',
}),
],
});
12. 持久化缓存
Webpack 5 引入了持久化缓存功能,可以显著提高重复构建的速度:
module.exports = merge(common, {
// ... 其他配置
cache: {
type: 'filesystem',
buildDependencies: {
config: [__filename],
},
name: 'production-cache',
},
});
13. 优化 CSS
使用 PurgeCSS 移除未使用的 CSS:
const PurgeCSSPlugin = require('purgecss-webpack-plugin');
const glob = require('glob');
const path = require('path');
module.exports = merge(common, {
// ... 其他配置
plugins: [
// ... 其他插件
new PurgeCSSPlugin({
paths: glob.sync(`${path.join(__dirname, 'src')}/**/*`, { nodir: true }),
safelist: ['body', 'html'],
}),
],
});
14. 使用 DLL Plugin 进一步优化
虽然 Webpack 5 的缓存机制已经很好,但对于特别大的项目,DLL 仍然有用:
const path = require('path');
const webpack = require('webpack');
module.exports = {
mode: 'production',
entry: {
vendor: ['react', 'react-dom', 'lodash'],
},
output: {
path: path.join(__dirname, 'dist', 'dll'),
filename: '[name].dll.js',
library: '[name]_[fullhash]',
},
plugins: [
new webpack.DllPlugin({
path: path.join(__dirname, 'dist', 'dll', '[name]-manifest.json'),
name: '[name]_[fullhash]',
}),
],
};
然后在主配置中引用:
const webpack = require('webpack');
module.exports = merge(common, {
// ... 其他配置
plugins: [
// ... 其他插件
new webpack.DllReferencePlugin({
manifest: require('./dist/dll/vendor-manifest.json'),
}),
],
});
15. 使用 SplitChunksPlugin 的高级配置
module.exports = merge(common, {
// ... 其他配置
optimization: {
// ... 其他优化
splitChunks: {
chunks: 'all',
maxInitialRequests: 30,
minSize: 20000,
cacheGroups: {
defaultVendors: {
test: /[\\/]node_modules[\\/]/,
priority: -10,
reuseExistingChunk: true,
},
framework: {
test: /[\\/]node_modules[\\/](react|react-dom|redux|react-redux)[\\/]/,
name: 'framework',
priority: 40,
reuseExistingChunk: true,
},
ui: {
test: /[\\/]node_modules[\\/](antd|@ant-design)[\\/]/,
name: 'ui',
priority: 30,
reuseExistingChunk: true,
},
utils: {
test: /[\\/]node_modules[\\/](lodash|moment|axios)[\\/]/,
name: 'utils',
priority: 20,
reuseExistingChunk: true,
},
default: {
minChunks: 2,
priority: -20,
reuseExistingChunk: true,
},
},
},
},
});
16. 使用 Webpack 5 的资产模块类型
module.exports = merge(common, {
// ... 其他配置
module: {
rules: [
// ... 其他规则
{
test: /\.svg$/,
type: 'asset/inline', // 内联为 Data URL
},
{
test: /\.(png|jpg|jpeg|gif)$/i,
type: 'asset', // 自动选择 inline 或 resource
parser: {
dataUrlCondition: {
maxSize: 4 * 1024, // 4kb
},
},
},
{
test: /\.(woff|woff2|eot|ttf|otf)$/i,
type: 'asset/resource', // 输出单独文件
},
],
},
});
通过以上这些优化措施,你可以显著提高 Webpack 5 在生产环境中的打包效率和输出质量。根据项目的具体需求,可以选择性地应用这些优化策略。