深入Webpack Plugin开发与常用Plugin推荐
在前一篇文章中,我们已经介绍了什么是 Webpack 插件以及与 Loader 的区别。本文将深入探讨 Webpack 插件,介绍 Webpack 的生命周期,如何自定义插件,并最后推荐一些常用的 Webpack 插件。
注意: 本文基于 Webpack 5,因为 Webpack 5 在发布一段时间后已经变得相对稳定。当然,现在最流行的构建工具可能是 Vite,其开发和编译速度比 Webpack 快数倍,但 Webpack 插件生态相对丰富。
# Webpack 的生命周期
因为插件是 Webpack 生态系统的关键组成部分,它们伴随 Webpack 的整个生命周期运行。Webpack 在达到特定阶段时提供了钩子(hook),用于触发插件的执行。因此,了解 Webpack 的生命周期对于编写插件至关重要。
Webpack 中最主要的两个对象,Compiler 和 Compilation。
Compiler 管理 Webpack 的整个生命周期,代表完整的 Webpack 的环境配置,具体可以点击此处前往 (opens new window),
Compilation 代表了一次资源版本构建,包含了当前的模块资源、编译生成资源、变化的文件以及被跟踪依赖的状态信息。
这里列举一些非常常用的 hooks:
entryOption
,当入口文件 entry 被处理之后调用beforeRun
,当 Webpack 读取完配置后,准备开始编译前调用run
,开始编译之后,读取 records 之前watchRun
, 只有在监听模式下,才会触发,例如修改了一些 js 文件,触发重新编译的时候,webpack-dev-server
就会调用。beforeCompile
,compilation
参数创建之后执行compile
,在一个新的compilation
创建之前执行thisCompilation
,初始化compilation
时调用,这时候回调函数中就能访问到compilation
对象compilation
也自己的生命周期,可以点击此处前往 (opens new window),列举一些常用的 hooksbuildModule
,模块构建开始之前触发,可以用来修改模块seal
,编译(compilation
)停止接收新模块时触发optimize
,优化阶段开始时触发optimizeModules
,模块的优化optimizeChunks
,优化 chunksadditionalAssets
,为编译(compilation)创建附加资源(asset)optimizeChunkAssets
,优化所有 chunk 资源(asset)optimizeAssets
,优化存储在 compilation.assets 中的所有资源 (asset)
compilation
,创建了 compilation 之后执行。emit
,输出 asset 到 output 目录之前执行assetEmitted
,在 asset 被输出时执行。此钩子可以访问被输出的 asset 的相关信息
compiler.hooks.assetEmitted.tap(
'MyPlugin',
(file, { content, source, outputPath, compilation, targetPath }) => {
console.log(content); // <Buffer 66 6f 6f 62 61 72>
}
);
done
, 在 compilation 完成时执行。
通过这段代码就可以看到 hooks 的执行顺序
const webpack = require('webpack');
const webpackConfig = require('./config/webpack.common.js');
const compiler = webpack(webpackConfig());
Object.keys(compiler.hooks).forEach((hookName) => {
if (compiler.hooks[hookName].tap) {
compiler.hooks[hookName].tap('anyString', () => {
console.log(`run -> ${hookName}`);
});
}
});
compiler.run();
以上就是 Webpack 的生命周期,其实整个 Webpack 的生命周期可以分为三个阶段。
- 准备阶段,这个阶段主要准备环境和配置参数来创建
Compiler
和Compilation
对象,到了thisCompilation
创建完对象之后就进入第二阶段 编译
阶段,这个阶段主要做 modules 的解析,生成chunks
- 产出阶段,这个阶段就是根据
chunks
去输出到文件目录中,模板Hash
更新,模板渲染chunk
,生成文件。
# 自定义 Plugin
如果想要深入前端技术,开发前端基建提高工作效率是必不可少的事情。
对于如何编写 Plugin,官方也提供了文档参考 (opens new window)
主要是实现两个方法,constructor
和apply
constructor 是让传入参数,初始化 Plugin。
apply 就是真正实现 Plugin 的作用,它提供一个compiler
对象,这个对象通过 hooks 来监听 Webpack 的各个阶段。最常用的就是监听thisCompilation
这个时候参数就会多一个compilation
对象,这时候就能监听compilation
的生命周期。
一般参照官方的插件去实现自己的需求。
一些自定义插件的写法,下面的插件主要是拉取 types 的内容,解压包并放到 node_modules 里面。
const fs = require('fs');
const path = require('path');
const http = require('http');
const compressing = require('compressing');
const appDirectory = fs.realpathSync(process.cwd());
const resolveApp = relativePath => path.resolve(appDirectory, relativePath);
module.exports = class MFTypesInjectPlugin {
constructor(props) {
const {
name,
typesZipUrl,
} = props;
this.name = name;
this.typesZipUrl = typesZipUrl;
}
apply(compiler) {
compiler.hooks.watchRun.tapAsync(
'MFTypesInjectPlugin',
(compiler, callback) => {
if (process.env.NODE_ENV === 'development') {
const zipFile = resolveApp(path.resolve('node_modules', `${this.name}_types.zip`));
const unzipPath = resolveApp(path.resolve('node_modules', '@types', `${this.name}_temp`));
const target = resolveApp(path.resolve('node_modules', '@types', this.name));
deleteFolder(unzipPath);
deleteFolder(target);
fs.mkdirSync(unzipPath, { recursive: true });
downloadFile(this.typesZipUrl, zipFile, () => {
compressing.zip.uncompress(zipFile, unzipPath).then(() => {
fs.renameSync(path.resolve(unzipPath, 'types'), target);
}).finally(() => {
callback();
});
});
} else {
callback();
}
}
);
}
}
... // 剩下的代码就不展示了,不然篇幅太长
# 常用的 Plugin 推荐
插件名 | 官方文档 | 作用 |
---|---|---|
HtmlWebpackPlugin | https://webpack.js.org/plugins/html-webpack-plugin | 自动生成 HTML 文件,引入打包后的资源,简化配置和构建过程。 |
MiniCssExtractPlugin | https://webpack.js.org/plugins/mini-css-extract-plugin/ | 将 CSS 从 JS 中提取为单独的文件,实现 CSS 代码的分离与优化。一般打正式包的时候才会使用,开发环境使用 style-loader |
BundleAnalyzerPlugin | https://github.com/webpack-contrib/webpack-bundle-analyzer | 文件分析插件,可以用于打包后资源的依赖及大小分析 |
CompressionWebpackPlugin | https://webpack.js.org/plugins/compression-webpack-plugin/ | 用于在构建过程中压缩和优化生成的 JavaScript 和 CSS 文件,减小文件大小,提高加载速度。一般用于 GZIP 之类的,但现在云厂商会自动开启 |
TerserWebpackPlugin | https://webpack.js.org/plugins/terser-webpack-plugin/ | 用于压缩和混淆 JavaScript 代码,减小文件体积,提高网页加载速度。Webpack5 默认开启,只要配置 minimize:true。如果要配置的话,还是要安装 |
CssMinimizerWebpackPlugin | https://webpack.js.org/plugins/css-minimizer-webpack-plugin/ | 用于压缩和优化 CSS 代码,减小样式文件的大小,提高页面加载速度。 |
DefinePlugin | https://webpack.js.org/plugins/define-plugin/ | 用于在编译过程中定义全局常量,可以在代码中使用这些常量,用于配置环境变量或进行条件编译。 |