tapable 的作用类似与 EventEmitter ,将内部事件分发到对应的 plugin 上。
Hook
tapable 提供了多种 hook,根据返回值可以分为下面几类:
- Basic:按顺序调用 tap 的函数
- Waterfall:和上面类似,不过会把上一个函数的返回值传给下一个函数
- Bail:可以提前退出,当某个函数返回任意值以后
- Loop:当某个函数返回非 undefined 后,重新从第一个函数执行,直到所有函数返回 undefined
hook 还可以是同步或者异步(异步意思是只有在用户 callback 执行了以后才继续下一个,参考下面的 async 的代码)的:
- Sync:这种 hook 只提供
tap()
方法 - AsyncSeries:提供
tap()
,tapAsync()
,tapPromise()
三种方法,回调函数是按同步顺序执行的 - AsyncParallel:提供
tap()
,tapAsync()
,tapPromise()
三种方法,回调函数是按并行执行的
现在通过名称来看看 AsyncSeriesWaterfallHook
,就知道它们是什么意思了。
例子
const {SyncHook, AsyncParallelHook} = require('tapable')
class Car {
constructor() {
this.hooks = {
accelerate: new SyncHook(["newSpeed"]),
brake: new SyncHook(),
calculateRoutes: new AsyncParallelHook(
["source", "target", "routesList"]
)
}
}
setSpeed(newSpeed) {
// 通过 call 来触发回调
this.hooks.accelerate.call(newSpeed)
}
}
const myCar = new Car()
// 通过 tap 来注册回调
myCar.hooks.brake.tap("WarningLampPlugin", () => console.log('warning lamp.on()'));
myCar.setSpeed("100km/h")
源码
源代码里面用了一些技巧来生成代码,这个不是很关注。下面来看一下具体每个 hooks 是怎么执行的
SyncHook
这个应该算是最基本的 hook 了,假设我们注册了 2 个 SyncHook
myCar.hooks.accelerate.tap("LoggerPlugin", newSpeed =>
console.log(`Accelerated to ${newSpeed}`));
myCar.hooks.accelerate.tap("ErrorLampPlugin", () => console.log('warning lamp.on()'));
那么 call 的时候生成的函数就是下面这个
function anonymous(newSpeed) {
"use strict";
var _context;
var _x = this._x;
var _fn0 = _x[0];
_fn0(newSpeed);
var _fn1 = _x[1];
_fn1(newSpeed);
}
SyncWaterfallHook
和 SyncHook 的区别就是输入为前一个回调的返回值
function anonymous(newSpeed) {
"use strict";
var _context;
var _x = this._x;
var _fn0 = _x[0];
var _result0 = _fn0(newSpeed);
if (_result0 !== undefined) {
newSpeed = _result0;
}
var _fn1 = _x[1];
var _result1 = _fn1(newSpeed);
if (_result1 !== undefined) {
newSpeed = _result1;
}
return newSpeed;
}
SyncBailHook
一旦返回值不为 undefined ,那么直接退出
function anonymous(newSpeed) {
"use strict";
var _context;
var _x = this._x;
var _fn0 = _x[0];
var _result0 = _fn0(newSpeed);
if (_result0 !== undefined) {
return _result0;
;
} else {
var _fn1 = _x[1];
var _result1 = _fn1(newSpeed);
if (_result1 !== undefined) {
return _result1;
;
} else {
}
}
}
AsyncSeriesWaterfallHook
注册两个回调
myCar.hooks.calculateRoutes.tapAsync("BingMapPlugin", (source, target, routesList, callback) => {
console.log('bing find', source, target, routesList);
routesList.push({ address: 'aaaa Inc' });
callback();
});
myCar.hooks.calculateRoutes.tapAsync("GoogleMapPlugin", (source, target, routesList, callback) => {
console.log('google find', source, target, routesList);
routesList.push({ address: 'google Inc' });
callback();
});
看看生成了什么代码
function anonymous(source, target, routesList, _callback
) {
"use strict";
var _context;
var _x = this._x;
function _next0() {
var _fn1 = _x[1];
_fn1(source, target, routesList, (function (_err1, _result1) {
if (_err1) {
_callback(_err1);
} else {
if (_result1 !== undefined) {
source = _result1;
}
_callback(null, source);
}
}));
}
var _fn0 = _x[0];
_fn0(source, target, routesList, (function (_err0, _result0) {
if (_err0) {
_callback(_err0);
} else {
if (_result0 !== undefined) {
source = _result0;
}
_next0();
}
}));
}
AsyncParallelHook
看看怎么实现并行的
function anonymous(source, target, routesList, _callback) {
"use strict";
var _context;
var _x = this._x;
do {
var _counter = 2;
var _done = (function () {
_callback();
});
if (_counter <= 0) break;
var _fn0 = _x[0];
_fn0(source, target, routesList, (function (_err0) {
if (_err0) {
if (_counter > 0) {
_callback(_err0);
_counter = 0;
}
} else {
if (--_counter === 0) _done();
}
}));
if (_counter <= 0) break;
var _fn1 = _x[1];
_fn1(source, target, routesList, (function (_err1) {
if (_err1) {
if (_counter > 0) {
_callback(_err1);
_counter = 0;
}
} else {
if (--_counter === 0) _done();
}
}));
} while (false);
}
哦,就是每次在结束回调里判断计数器有没有为0