Tapable 是一个类似于 Node.js 的 EventEmitter 的库, 主要是控制钩子函数的发布 与订阅,控制着 webpack 的插件系统。
Tapable库暴露了很多 Hook(钩子)类,为插件提供挂载的钩子
const {
SyncHook, //同步钩子
SyncBailHook, //同步熔断钩子 即return一个非undefined的值,则不再继续执行后面的监听函数
SyncWaterfallHook, //同步流水钩子 上一个监听函数的返回值会传递给下一个监听函数
SyncLoopHook, //同步循环钩子 同步遇到某个不返回undefined的监听函数,就重复执行
AsyncParallelHook, //异步并发钩子 会并发执行两个异步的挂载函数,等两个都执行结束了,钩子函数执行自己的callback
AsyncParallelBailHook, //异步并发熔断钩子 两个挂载的异步函数虽然是异步执行的,但是只要有一个callback 传入一个非undefined的值,钩子函数便会执行自己的callback,还没有执行完成的挂载还是会接着走,不会被停止
AsyncSeriesHook, //异步串行钩子 两个异步函数将串行执行,第一个挂载函数成功后,才开始执行第二个挂载函数
AsyncSeriesBailHook, //异步串行熔断钩子 钩子函数的异步挂载函数按顺序执行,当第一个执行的resolve 返回一个非undefined的值的时候,后面的串行异步挂载函数将不执行,钩子的回调函数将立马执行。
AsyncSeriesWaterfallHook //异步串行流水钩子 上一个监听函数的返回值会传递给下一个监听函数
} = require("tapable");
HOOK,所有钩子的后缀
Waterfall 同步方法,但是它会传值给下一个函数
Bail 熔断:当函数没有任何返回值,就会在当面执行函数停止
loop:监听函数返回true表示继续循环,返回undefine表示结束循环
sync 同步方法
AsyncSeries 异步串行钩子
AsyncParallel 异步并行执行钩子
先看一段代码
核心对象 Compiler 继承 Tapable
class Compiler extends Tapable { // ... }
核心对象 Compilation 继承 Tapable
class Compilation extends Tapable { // ... }
1、new Hook 新建钩子
2、class 接受数组参数 options ,非必传。类方法会根据传参,接受同样数量的参数。 const hook1 = new SyncHook(["arg1", "arg2", "arg3"])
3、钩子的绑定与执行
Tabpack 提供了同步&异步绑定钩子的方法,并且他们都有绑定事件和执行事件对 应的方法。
异步
绑定:tapAsync/tapPromise/tap
执行:callAsync/promise
同步:
tap
call
4、hook 基本用法示例
const {
SyncHook
} = require('tapable');
const hook = new SyncHook(['arg1', 'arg2', 'arg3']);
hook.tap('hook1', (arg1, arg2, arg3) => {
console.log(arg1, arg2, arg3);
});
hook.call(1, 2, 3);
5、实际例子演示
定义一个 Car 方法,在内部 hooks 上新建钩子。分别是同步钩子 accelerate、 brake( accelerate 接受一个参数)、异步钩子 calculateRoutes
使用钩子对应的绑定和执行方法
calculateRoutes 使用 tapPromise 可以返回一个 promise 对象
6、例子
const {
SyncHook,
AsyncSeriesHook
} = require('tapable');
class Car {
constructor() {
this.hooks = {
accelerate: new SyncHook(['newspeed']),
brake: new SyncHook(),
calculateRoutes: new AsyncSeriesHook(["source", "target", "routesList"])
}
}
}
const myCar = new Car();
//绑定同步钩子
myCar.hooks.brake.tap("WarningLampPlugin", () => console.log('WarningLampPlugin'));
//绑定同步钩子 并传参
myCar.hooks.accelerate.tap("LoggerPlugin", newSpeed => console.log(`Accelerating to ${newSpeed}`));
//绑定一个异步Promise钩子
myCar.hooks.calculateRoutes.tapPromise("calculateRoutes tapPromise", (source, target, routesList, callback) => {
// return a promise
return new Promise((resolve,reject)=>{
setTimeout(()=>{
console.log(`tapPromise to ${source} ${target} ${routesList}`)
resolve();
},1000)
})
});
myCar.hooks.brake.call();
myCar.hooks.accelerate.call(10);
console.time('cost');
//执行异步钩子
myCar.hooks.calculateRoutes.promise('Async', 'hook', 'demo').then(() => {
console.timeEnd('cost');
}, err => {
console.error(err);
重点开plugins数组,里面是对象还是函数;如是函数执行call传入this和参数都是compile、如是对象执行对象的apply方法
if (Array.isArray(options)) {
compiler = new MultiCompiler(options.map(options => webpack(options)));
} else if (typeof options === "object") {
options = new WebpackOptionsDefaulter().process(options);
compiler = new Compiler(options.context);
compiler.options = options;
new NodeEnvironmentPlugin().apply(compiler);
if (options.plugins && Array.isArray(options.plugins)) {
for (const plugin of options.plugins) {
if (typeof plugin === "function") {
plugin.call(compiler, compiler);
} else {
plugin.apply(compiler);
}
}
}
compiler.hooks.environment.call();
compiler.hooks.afterEnvironment.call();
compiler.options = new WebpackOptionsApply().process(options, compiler);
}
const {
SyncHook,
AsyncSeriesHook
} = require('tapable');
module.exports = class Compiler {
constructor() {
this.hooks = {
accelerate: new SyncHook(['newspeed']),
brake: new SyncHook(),
calculateRoutes: new AsyncSeriesHook(["source", "target", "routesList"])
}
}
run(){
this.accelerate(10)
this.break()
this.calculateRoutes('Async', 'hook', 'demo')
}
accelerate(speed) {
this.hooks.accelerate.call(speed);
}
break() {
this.hooks.brake.call();
}
calculateRoutes() {
this.hooks.calculateRoutes.promise(...arguments).then(() => {
}, err => {
console.error(err);
});
}
}