Tapable

一、Tapable 是什么?


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"); 


二、Tapable hooks 类型


HOOK,所有钩子的后缀
Waterfall 同步方法,但是它会传值给下一个函数
Bail 熔断:当函数没有任何返回值,就会在当面执行函数停止
loop:监听函数返回true表示继续循环,返回undefine表示结束循环

sync 同步方法
AsyncSeries 异步串行钩子
AsyncParallel 异步并行执行钩子



三、看下webpack

 


先看一段代码 
核心对象 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);
 

 

 

 

四、Tapable 是如何和 webpack 联系起来的?



重点开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); 
}
 


五、模拟实现一个 Compiler.js

 



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);
});
}
}

你可能感兴趣的:(webpack,webpack,前端)