Webpack
插件(Plugins)
在 Webpack 的构建流程中,插件(Plugins)是扩展和定制功能的核心机制。它们可以在编译器的不同阶段插入钩子,实现诸如生成 HTML、提取静态资源、优化体积、压缩代码、复制文件等功能。常用插件既包括官方内置(或官方维护)的插件,也包括社区提供的高效第三方插件。
核心功能插件
HtmlWebpackPlugin
简化生成 HTML 文件,将打包后的脚本自动注入到 HTML 中,支持自定义模板与生产环境最小化处理。它尤其适用于名称带 hash 的文件,确保每次编译都能正确引用最新脚本。
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
// ... existing code ...
plugins: [
new HtmlWebpackPlugin({
title: '应用名称',
template: './src/index.html',
filename: 'index.html',
minify: {
collapseWhitespace: true,
removeComments: true,
},
// 注入选项: true | 'body' | 'head' | false
inject: true,
}),
],
}
MiniCssExtractPlugin
将 CSS 从 JS 中分离,生成独立的 CSS 文件,支持按需加载与 SourceMap,非常适合生产环境的样式提取。
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
module.exports = {
// ... existing code ...
module: {
rules: [
{
test: /\.css$/,
use: [
{
loader: MiniCssExtractPlugin.loader,
options: {
publicPath: '../',
},
},
'css-loader',
],
},
],
},
plugins: [
new MiniCssExtractPlugin({
filename: 'css/[name].[contenthash:8].css',
chunkFilename: 'css/[id].[contenthash:8].css',
}),
],
}
CleanWebpackPlugin
在每次构建前清理输出目录,删除旧的无用文件,保持 dist 文件夹干净,防止冗余资源累积
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
module.exports = {
// ... existing code ...
plugins: [
new CleanWebpackPlugin({
// 不删除的文件或文件夹
cleanOnceBeforeBuildPatterns: ['**/*', '!static-files/**'],
// 在每次构建前清理匹配的文件
cleanStaleWebpackAssets: true,
// 在控制台输出日志
verbose: true,
}),
],
}
优化相关插件
TerserWebpackPlugin
使用 Terser 压缩 JavaScript,支持 ES6+ 语法,Webpack 5 默认集成,Webpack 4 需要单独安装。
const TerserPlugin = require('terser-webpack-plugin')
module.exports = {
// ... existing code ...
optimization: {
minimize: true,
minimizer: [
new TerserPlugin({
terserOptions: {
compress: {
drop_console: true, // 移除 console
drop_debugger: true, // 移除 debugger
},
format: {
comments: false, // 移除注释
},
},
extractComments: false, // 不将注释提取到单独的文件
parallel: true, // 使用多进程并行运行
}),
],
},
}
CssMinimizerWebpackPlugin
基于 cssnano 优化与压缩 CSS,支持并行与缓存,比 optimize-css-assets-webpack-plugin 更完善。
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
module.exports = {
// ... existing code ...
module: {
rules: [
{
test: /\.css$/,
use: [MiniCssExtractPlugin.loader, 'css-loader'],
},
],
},
optimization: {
minimizer: [
'...', // 继承默认配置
new CssMinimizerPlugin({
minimizerOptions: {
preset: [
'default',
{
discardComments: { removeAll: true },
},
],
},
parallel: true, // 启用多进程并行处理
}),
],
},
plugins: [new MiniCssExtractPlugin()],
}
SplitChunksPlugin
内置插件,负责自动拆分共享模块,减少重复代码并实现缓存优化。自 webpack 4 起取代 CommonsChunkPlugin。
module.exports = {
// ... existing code ...
optimization: {
splitChunks: {
chunks: 'all', // 对所有模块都进行分割
minSize: 20000, // 生成 chunk 的最小体积
minRemainingSize: 0,
minChunks: 1, // 拆分前必须共享模块的最小 chunks 数
maxAsyncRequests: 30, // 最大异步请求数
maxInitialRequests: 30, // 最大初始化请求数
enforceSizeThreshold: 50000,
cacheGroups: {
defaultVendors: {
test: /[\\/]node_modules[\\/]/,
priority: -10,
reuseExistingChunk: true,
name: 'vendors',
},
default: {
minChunks: 2,
priority: -20,
reuseExistingChunk: true,
},
},
},
},
}
HtmlMinimizerWebpackPlugin
使用多种底层工具(如 html-minifier-terser、@swc/html、@minify-html/node)对 HTML 进行压缩、最小化。
const HtmlMinimizerPlugin = require('html-minimizer-webpack-plugin')
module.exports = {
// ... existing code ...
optimization: {
minimize: true,
minimizer: [
'...', // 继承默认配置
new HtmlMinimizerPlugin({
minimizerOptions: {
collapseWhitespace: true,
removeComments: true,
removeRedundantAttributes: true,
removeScriptTypeAttributes: true,
removeStyleLinkTypeAttributes: true,
useShortDoctype: true,
},
parallel: true, // 启用多进程并行处理
}),
],
},
}
CompressionWebpackPlugin
生成 Gzip 或 Brotli 等压缩资源,便于 HTTP 服务端以 Content-Encoding 交付,提升传输效率。
const CompressionPlugin = require('compression-webpack-plugin')
module.exports = {
// ... existing code ...
plugins: [
new CompressionPlugin({
algorithm: 'gzip', // 压缩算法
test: /\.(js|css|html|svg)$/, // 匹配文件
threshold: 10240, // 只处理大于此大小的资源(以字节为单位)
minRatio: 0.8, // 只有压缩率小于这个值的资源才会被处理
deleteOriginalAssets: false, // 是否删除原始资源
}),
],
}
ImageMinimizerWebpackPlugin
集成 imagemin,自动优化(压缩)图片资源,减小体积,适合有大量图片的项目。
const ImageMinimizerPlugin = require('image-minimizer-webpack-plugin')
module.exports = {
// ... existing code ...
optimization: {
minimizer: [
'...', // 继承默认配置
new ImageMinimizerPlugin({
minimizer: {
implementation: ImageMinimizerPlugin.imageminMinify,
options: {
plugins: [
['gifsicle', { interlaced: true }],
['jpegtran', { progressive: true }],
['optipng', { optimizationLevel: 5 }],
['svgo', { plugins: [{ removeViewBox: false }] }],
],
},
},
// 启用文件缓存
cache: true,
// 仅处理更改的图像
filter: (source) => {
return source.byteLength > 8192 // 只处理大于 8kb 的图像
},
}),
],
},
}
资源管理插件
CopyWebpackPlugin
将项目中的静态文件或目录复制到输出目录,如字体、图标、静态 HTML 等。
const CopyPlugin = require('copy-webpack-plugin')
module.exports = {
// ... existing code ...
plugins: [
new CopyPlugin({
patterns: [
{
from: 'public', // 源目录
to: 'assets', // 目标目录
globOptions: {
ignore: ['**/index.html'], // 忽略的文件
},
},
{
from: 'src/static/favicon.ico',
to: 'favicon.ico',
},
],
}),
],
}
DefinePlugin
内置插件,可在编译时注入全局常量(如 process.env.NODE_ENV),方便不同环境下的条件编译,无需额外安装。
const webpack = require('webpack')
module.exports = {
// ... existing code ...
plugins: [
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV),
'process.env.API_URL': JSON.stringify('https://api.example.com'),
VERSION: JSON.stringify('1.0.0'),
PRODUCTION: process.env.NODE_ENV === 'production',
DEBUG: false,
}),
],
}
ProvidePlugin
自动加载模块,当代码中出现特定变量时,无需 import 即可使用(如 $ 对应 'jquery'),简化全局依赖。
const webpack = require('webpack')
module.exports = {
// ... existing code ...
plugins: [
new webpack.ProvidePlugin({
$: 'jquery',
jQuery: 'jquery',
'window.jQuery': 'jquery',
_: 'lodash',
React: 'react',
}),
],
}
分析与其他插件
BundleAnalyzerPlugin
可视化打包结果,生成交互式树状图,帮助分析各模块体积与依赖,定位体积热点。(需安装 webpack-bundle-analyzer)。
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer')
module.exports = {
// ... existing code ...
plugins: [
new BundleAnalyzerPlugin({
analyzerMode: 'server', // 'server'|'static'|'json'|'disabled'
analyzerHost: '127.0.0.1',
analyzerPort: 8888,
reportFilename: 'report.html',
defaultSizes: 'parsed', // 'stat'|'parsed'|'gzip'
openAnalyzer: true,
generateStatsFile: false,
statsFilename: 'stats.json',
}),
],
}
ForkTsCheckerWebpackPlugin
在 TypeScript 项目中异步执行类型检查与 ESLint 校验,避免编译阻塞,加快开发模式下的构建速度。
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin')
module.exports = {
// ... existing code ...
plugins: [
new ForkTsCheckerWebpackPlugin({
typescript: {
configFile: './tsconfig.json',
diagnosticOptions: {
semantic: true,
syntactic: true,
},
mode: 'write-references', // 'write-references'|'readonly'
},
eslint: {
files: './src/**/*.{ts,tsx,js,jsx}',
options: {
fix: true,
},
},
issue: {
include: [{ file: '../**/src/**/*.{ts,tsx}' }],
exclude: [{ file: '**/*.spec.ts' }],
},
}),
],
}
EslintWebpackPlugin
在构建中自动执行 ESLint 检查,可修复部分问题并在命令行输出错误/警告,保持代码质量。
const ESLintPlugin = require('eslint-webpack-plugin')
module.exports = {
// ... existing code ...
plugins: [
new ESLintPlugin({
context: './src',
extensions: ['js', 'jsx', 'ts', 'tsx'],
exclude: ['node_modules', 'dist'],
fix: true, // 自动修复
emitWarning: true, // 将警告作为警告而不是错误输出
failOnError: process.env.NODE_ENV === 'production', // 生产环境下出错时停止构建
quiet: false, // 不隐藏警告
}),
],
}
文件指纹
文件指纹(File Fingerprint)是指在打包后的文件名中添加一串 hash 值,用于标识文件内容的变化。这对于浏览器缓存优化非常重要,因为当文件内容发生变化时,文件名也会改变,从而强制浏览器重新加载新文件。
文件指纹的类型
- Hash:整个项目的 hash 值,项目文件有变化时,所有文件的 hash 值都会改变
- Chunkhash:根据不同的入口文件(Entry)进行依赖文件解析,构建对应的 chunk,生成对应的 hash 值
- Contenthash:根据文件内容生成 hash 值,文件内容不变,hash 值不变
文件指纹的配置
// webpack.config.js
module.exports = {
output: {
filename: '[name].[chunkhash:8].js', // JS 文件指纹
path: path.resolve(__dirname, 'dist'),
},
module: {
rules: [
{
test: /\.css$/,
use: [MiniCssExtractPlugin.loader, 'css-loader'],
},
],
},
plugins: [
new MiniCssExtractPlugin({
filename: '[name].[contenthash:8].css', // CSS 文件指纹
}),
new CleanWebpackPlugin(),
],
}
图片文件指纹
// webpack.config.js
module.exports = {
module: {
rules: [
{
test: /\.(png|svg|jpg|gif)$/,
use: [
{
loader: 'file-loader',
options: {
name: '[name].[hash:8].[ext]', // 图片文件指纹
},
},
],
},
],
},
}
最佳实践
JS 文件使用 chunkhash
- 原因:JS 文件通常由 webpack 打包生成,使用 chunkhash 可以确保只有相关文件变化时才会改变 hash 值
CSS 文件使用 contenthash
- 原因:CSS 文件内容变化时才会改变 hash 值,避免不必要的缓存失效
图片等静态资源使用 hash
- 原因:图片等静态资源通常不会频繁变化,使用 hash 可以确保内容变化时更新缓存
实际应用示例
// webpack.config.js
const path = require('path')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
module.exports = {
entry: {
app: './src/index.js',
vendor: './src/vendor.js',
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].[chunkhash:8].js',
chunkFilename: '[name].[chunkhash:8].js',
},
module: {
rules: [
{
test: /\.css$/,
use: [MiniCssExtractPlugin.loader, 'css-loader'],
},
{
test: /\.(png|svg|jpg|gif)$/,
use: [
{
loader: 'file-loader',
options: {
name: '[name].[hash:8].[ext]',
outputPath: 'images/',
},
},
],
},
],
},
plugins: [
new CleanWebpackPlugin(),
new MiniCssExtractPlugin({
filename: '[name].[contenthash:8].css',
chunkFilename: '[id].[contenthash:8].css',
}),
],
optimization: {
splitChunks: {
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
chunks: 'all',
filename: '[name].[chunkhash:8].js',
},
},
},
},
}
注意事项
- 确保在生产环境中使用文件指纹
- 合理设置 hash 长度,通常 8 位就足够了
- 注意文件指纹对开发环境的影响,建议在开发环境禁用文件指纹
- 使用
clean-webpack-plugin
清理旧的构建文件 - 合理配置
splitChunks
优化代码分割
开发环境配置
// webpack.dev.js
module.exports = {
mode: 'development',
output: {
filename: '[name].js', // 开发环境不使用文件指纹
path: path.resolve(__dirname, 'dist'),
},
plugins: [
new MiniCssExtractPlugin({
filename: '[name].css', // 开发环境不使用文件指纹
}),
],
}
通过合理使用文件指纹,可以:
- 优化浏览器缓存策略
- 提高页面加载速度
- 确保用户始终使用最新的文件版本
- 减少不必要的网络请求