Appearance
Webpack 插件(Plugins)
插件是 Webpack 生态系统的核心,它们提供了一种强大而灵活的方式来定制 Webpack 构建过程的几乎每个方面。插件可以执行各种任务,如打包优化、资源管理和注入环境变量。
插件基础概念
插件通过 Webpack 的生命周期钩子(hooks)系统来影响构建过程。它们通常是一个带有 apply 方法的对象,该方法在 Webpack 编译器实例被激活时被调用。
基础插件示例
javascript
// 自定义插件示例
class MyExampleWebpackPlugin {
apply(compiler) {
// Webpack 模块编译完成后执行
compiler.hooks.done.tap('MyExampleWebpackPlugin', (stats) => {
console.log('编译完成!');
});
// Webpack 编译开始前执行
compiler.hooks.compile.tap('MyExampleWebpackPlugin', (params) => {
console.log('开始编译...');
});
}
}
// 在配置中使用
module.exports = {
plugins: [
new MyExampleWebpackPlugin()
]
};
常用插件详解
1. HtmlWebpackPlugin
HtmlWebpackPlugin 用于生成 HTML 文件,并自动引入 Webpack 打包后的资源。
javascript
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
plugins: [
new HtmlWebpackPlugin({
title: 'My App', // 页面标题
filename: 'index.html', // 输出文件名
template: './src/index.html', // 模板文件
inject: true, // 自动注入资源
minify: { // 压缩配置
removeComments: true,
collapseWhitespace: true,
removeRedundantAttributes: true,
useShortDoctype: true,
removeEmptyAttributes: true,
removeStyleLinkTypeAttributes: true,
keepClosingSlash: true,
minifyJS: true,
minifyCSS: true,
minifyURLs: true
},
chunksSortMode: 'auto', // 资源排序方式
cache: true, // 启用缓存
showErrors: true // 显示错误
})
]
};
2. CleanWebpackPlugin
CleanWebpackPlugin 用于在每次构建前清理输出目录。
javascript
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
module.exports = {
plugins: [
new CleanWebpackPlugin({
dry: false, // 实际删除文件
verbose: true, // 控制台显示删除信息
cleanStaleWebpackAssets: true, // 清理过时的资产
protectWebpackAssets: true, // 保护 webpack 资产
cleanOnceBeforeBuildPatterns: ['**/*'], // 清理模式
cleanAfterEveryBuildPatterns: [] // 构建后清理模式
})
]
};
3. MiniCssExtractPlugin
MiniCssExtractPlugin 用于将 CSS 提取到单独的文件中。
javascript
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
plugins: [
new MiniCssExtractPlugin({
filename: 'styles/[name].[contenthash].css', // 输出文件名
chunkFilename: '[id].[contenthash].css', // 非入口 chunk 文件名
ignoreOrder: false // 启用 CSS 顺序警告
})
],
module: {
rules: [
{
test: /\.css$/,
use: [
MiniCssExtractPlugin.loader, // 替代 style-loader
'css-loader'
]
}
]
}
};
4. DefinePlugin
DefinePlugin 用于定义全局常量。
javascript
const webpack = require('webpack');
module.exports = {
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(require('./package.json').version),
'__DEBUG__': JSON.stringify(process.env.NODE_ENV === 'development')
})
]
};
5. CopyWebpackPlugin
CopyWebpackPlugin 用于复制文件和目录。
javascript
const CopyWebpackPlugin = require('copy-webpack-plugin');
module.exports = {
plugins: [
new CopyWebpackPlugin({
patterns: [
{
from: 'public/', // 源目录
to: '.', // 目标目录
globOptions: {
ignore: ['**/index.html'] // 忽略的文件
}
},
{
from: 'src/assets/',
to: 'assets/',
// 只在生产环境复制
noErrorOnMissing: true
}
]
})
]
};
高级插件配置
1. 环境特定插件
javascript
const isProduction = process.env.NODE_ENV === 'production';
const plugins = [
new HtmlWebpackPlugin({
template: './src/index.html'
})
];
if (isProduction) {
plugins.push(
new MiniCssExtractPlugin({
filename: 'styles/[name].[contenthash].css'
}),
new webpack.optimize.MinChunkSizePlugin({
minChunkSize: 10000
})
);
} else {
plugins.push(
new webpack.HotModuleReplacementPlugin()
);
}
module.exports = {
plugins
};
2. 多页面应用插件配置
javascript
const HtmlWebpackPlugin = require('html-webpack-plugin');
const htmlPages = ['home', 'about', 'contact'].map(page => {
return new HtmlWebpackPlugin({
template: `src/pages/${page}/index.html`,
filename: `${page}.html`,
chunks: [page], // 只包含特定 chunk
inject: true
});
});
module.exports = {
entry: {
home: './src/pages/home/index.js',
about: './src/pages/about/index.js',
contact: './src/pages/contact/index.js'
},
plugins: [
...htmlPages
]
};
3. 自定义插件开发
javascript
// 资源统计插件
class AssetStatsPlugin {
apply(compiler) {
compiler.hooks.emit.tapAsync('AssetStatsPlugin', (compilation, callback) => {
const assets = Object.keys(compilation.assets);
const stats = {};
assets.forEach(asset => {
const size = compilation.assets[asset].size();
stats[asset] = {
size: size,
sizeKB: (size / 1024).toFixed(2) + ' KB'
};
});
// 将统计信息写入文件
const statsJson = JSON.stringify(stats, null, 2);
compilation.assets['stats.json'] = {
source: () => statsJson,
size: () => statsJson.length
};
callback();
});
}
}
// 文件指纹插件
class FileHashPlugin {
apply(compiler) {
compiler.hooks.compilation.tap('FileHashPlugin', (compilation) => {
compilation.hooks.processAssets.tap(
{
name: 'FileHashPlugin',
stage: webpack.Compilation.PROCESS_ASSETS_STAGE_ANALYSE
},
(assets) => {
Object.keys(assets).forEach(filename => {
if (filename.endsWith('.js')) {
const asset = assets[filename];
const source = asset.source();
const hash = this.generateHash(source);
// 重命名文件以包含哈希
delete assets[filename];
assets[filename.replace(/(\.\w+)$/, `.${hash}$1`)] = asset;
}
});
}
);
});
}
generateHash(content) {
const crypto = require('crypto');
return crypto.createHash('md5').update(content).digest('hex').substring(0, 8);
}
}
优化相关插件
1. 代码分割插件
javascript
const webpack = require('webpack');
module.exports = {
optimization: {
splitChunks: {
chunks: 'all',
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
priority: 10,
chunks: 'all'
},
common: {
name: 'common',
minChunks: 2,
priority: 5,
chunks: 'all',
enforce: true
}
}
},
runtimeChunk: {
name: 'runtime'
}
},
plugins: [
// 模块联邦插件 (Webpack 5)
new webpack.container.ModuleFederationPlugin({
name: 'host_app',
remotes: {
remote_app: 'remote_app@http://localhost:3001/remoteEntry.js'
},
shared: ['react', 'react-dom']
})
]
};
2. 压缩优化插件
javascript
const TerserPlugin = require('terser-webpack-plugin');
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
module.exports = {
optimization: {
minimize: true,
minimizer: [
new TerserPlugin({
terserOptions: {
compress: {
drop_console: true, // 删除 console
drop_debugger: true, // 删除 debugger
pure_funcs: ['console.log'] // 删除特定函数调用
},
mangle: true,
format: {
comments: false // 删除注释
}
},
extractComments: false
}),
new CssMinimizerPlugin({
test: /\.css$/g,
parallel: true, // 并行处理
minimizerOptions: {
preset: [
'default',
{
discardComments: { removeAll: true }
}
]
}
})
]
}
};
3. 分析插件
javascript
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
module.exports = {
plugins: [
new BundleAnalyzerPlugin({
analyzerMode: 'static', // 静态 HTML 报告
openAnalyzer: false, // 不自动打开浏览器
reportFilename: 'bundle-report.html', // 报告文件名
defaultSizes: 'parsed' // 显示解析后的大小
})
]
};
性能优化插件
1. 缓存插件
javascript
module.exports = {
cache: {
type: 'filesystem', // 文件系统缓存
buildDependencies: {
config: [__filename] // 配置文件变更时清除缓存
},
cacheDirectory: path.resolve(__dirname, '.webpack-cache')
},
plugins: [
// HardSourceWebpackPlugin (适用于 Webpack 4)
// new HardSourceWebpackPlugin()
]
};
2. 进度条插件
javascript
const ProgressBarPlugin = require('progress-bar-webpack-plugin');
module.exports = {
plugins: [
new ProgressBarPlugin({
format: ' build [:bar] ' +
':percent ' +
'(:elapsed seconds)',
clear: false
})
]
};
小结
Webpack 插件系统提供了强大的功能扩展机制,通过插件我们可以定制构建过程的几乎所有方面。常用的插件包括 HTML 生成、资源清理、CSS 提取、环境变量定义等。理解插件的工作原理和配置方法,可以帮助我们构建更高效的开发环境和生产构建流程。