使用Plugin
安装插件, 创建插件即可
new cleanWebpackPlugin();
综上所述得出一个结论: 插件是一个类
Plugin的特点
clean-webpack-plugin, 会在打包之前清空指定目录
html-webpack-plugin, 会在打包之后拷贝HTML文件并将打包好的文件插入到HTML中
综上所述得出一个结论: 插件可以在打包过程中的特定阶段执行
大型框架实现在特点阶段执行插件代码
Frame通过Tapable在不同的阶段发送了不同的通知,
我们只需要在编写插件时注册我们需要监听的通知即可
综上所述: 只要会编写类, 只要会使用Tapable订阅消息, 就会写插件
*(Tapabale的原理及使用可参考我的“实现发布订阅模式”文章)
(plugin就是订阅者,它向发布者(frame)在特定阶段订阅消息,frame在特定阶段发布消息(执行此plugin))
插件的基本格式
class CustomPlugin {
constructor(options){
console.log("插件被创建了", options);
}
apply(compiler){
console.log("插件被执行了", options);
}
}
module.exports = CustomPlugin;
传入的compiler对象中除了包含webpack打包的配置以外,
还包含了webpack各阶段消息发布者
例如:
entryOption: 给webpack编译器传递配置文件之后
run: webpack编译器run方法被执行
emit: 打包文件写入之前
afterEmit: 打包文件写入之后
done: 打包完成
… …
实例
我们为webpack写一个简单的插件(webpack-clean-plugin),主要功能是清除之前所创建的目录及文件,代码如下:
const path = require("path");
const fs = require("fs");
class CleanWebpackPlugin {
constructor(options){
console.log("插件被创建了", options);
}
apply(compiler){
// 可以通过compiler对象的options拿到webpack的配置文件
let outputPath = compiler.options.output.path;
compiler.hooks.entryOption.tap("CleanWebpackPlugin", () => {
this.cleanDir(outputPath);
});
}
cleanDir(dirPath){
// 注意点: 在NodeJS中不能直接删除非空的目录
// 1.判断是否是一个非空的目录
if(fs.statSync(dirPath).isDirectory() && fs.readdirSync(dirPath).length !== 0){
// 2.如果是一个非空的目录, 那么就先删除这个目录中的内容
let files = fs.readdirSync(dirPath);
files.forEach((file)=>{
let filePath = path.resolve(dirPath, file);
if(fs.statSync(filePath).isDirectory()){
this.cleanDir(filePath);
}else{
fs.unlinkSync(filePath);
}
})
}
// 3.如果不是一个非空的目录, 那么就直接删除这个目录
fs.rmdirSync(dirPath);
}
}
module.exports = CleanWebpackPlugin;
我们来看这句核心代码:
compiler.hooks.entryOption.tap("CleanWebpackPlugin", () => {
this.cleanDir(outputPath);
});
plugin是订阅者,他向发布者(hooks,即webpack)订阅一个消息:在entryoption之后执行此plugin。
接下来我们便可以创建发布者发布消息。
安装tapable
npm install tapable
导入配置
const {
SyncHook,
SyncBailHook,
SyncWaterfallHook,
SyncLoopHook,
AsyncParallelHook,
AsyncParallelBailHook,
AsyncSeriesHook,
AsyncSeriesBailHook,
AsyncSeriesWaterfallHook
} = require("tapable");
创建发布者
this.hooks = {
entryOption: new SyncBailHook(),
}
获取插件, 调用插件的apply方法, 并且将当前的编译对象传递给插件
let plugins = this.config.plugins;
plugins.forEach((plugin) => {
plugin.apply(this);
});
在entryOption阶段发布(传递完配置文件后 )
cp.hooks.entryOption.call();
总结
1.创建订阅者,即编写plugin(向发布者指定在哪个阶段执行)
2.创建发布者,获取plugin的信息并传入配置
3.在某个阶段发布(即执行plugin)
至此,plugin的原理和实现完成。