Plugin
什么是 Plugin?
Plugin(插件)用于解决 Loader 无法实现的其他事。它的核心作用是干预 Webpack 的构建过程。
Webpack 在运行的生命周期中会广播出许多事件,Plugin 可以监听这些事件,在合适的时机通过 Webpack 提供的 API 改变输出结果、进行代码压缩、注入环境变量、输出构建统计信息等。
核心机制:Tapable
Webpack 的插件架构基于一个核心库 —— Tapable。它是一个增强版的发布订阅模式,提供了各种类型的 Hook(钩子):
- 同步钩子:如
SyncHook、SyncBailHook。 - 异步钩子:如
AsyncSeriesHook(串行)、AsyncParallelHook(并行)。
插件通过 tap(注册同步事件)、tapAsync(注册异步回调事件)或 tapPromise(注册 Promise 事件)将自己的逻辑注入到这些 Hook 中。
常见 Plugin
HtmlWebpackPlugin:自动生成 HTML 文件,并自动引入构建产物(JS/CSS)。MiniCssExtractPlugin:将 CSS 提取为独立的纯 CSS 文件(代替style-loader,适合生产环境)。DefinePlugin:在编译时创建全局常量(如process.env.NODE_ENV)。TerserWebpackPlugin:用于压缩 JavaScript(Webpack 5 生产环境默认自带)。CleanWebpackPlugin:每次构建前清理输出目录(Webpack 5 可直接配置output.clean: true)。
自定义 Plugin 面试要点
一个 Plugin 必须是一个带有 apply 方法的类。Webpack 初始化时,会遍历所有的 Plugin 实例并调用它们的 apply 方法,同时传入 compiler 对象。
基本结构:
class MyCustomPlugin {
constructor(options) {
this.options = options; // 接收配置参数
}
apply(compiler) {
// 注册 Webpack 的生命周期 Hook (同步)
compiler.hooks.done.tap("MyCustomPlugin", (stats) => {
console.log("构建完成!");
});
// 注册异步 Hook (例如 emit: 在输出 asset 到 output 目录之前执行)
compiler.hooks.emit.tapAsync("MyCustomPlugin", (compilation, callback) => {
// 操作 compilation.assets 修改输出资源
compilation.assets["hello.txt"] = {
source: function () {
return "Hello World!";
},
size: function () {
return 12; // 'Hello World!'.length
},
};
// 必须调用 callback,通知 Webpack 异步逻辑执行完毕,可以继续下一步
callback();
});
}
}
module.exports = MyCustomPlugin;
Compiler 和 Compilation 的区别(高频考点)
Compiler对象:- 代表了完整的 Webpack 环境配置。
- 每次启动 Webpack 构建时,只会创建一个
Compiler实例。 - 它包含了所有的配置信息(options、loaders、plugins 等),并暴露了 Webpack 整个生命周期的顶级钩子(如
make,compile,emit,done)。
Compilation对象:- 代表了一次单一的版本构建和生成过程。
- 在开发模式(watch 模式)下,每次文件发生变化触发重新编译时,都会创建一个新的
Compilation实例。 - 它包含了当前的模块资源、编译生成的资产(assets)、发生变化的文件等,并暴露了更细粒度的钩子(如
optimizeChunks,processAssets)。