以下是tapable暴露的所有hooks类型。
exports.__esModule = true;
exports.Tapable = require("./Tapable");
exports.SyncHook = require("./SyncHook");
exports.SyncBailHook = require("./SyncBailHook");
exports.SyncWaterfallHook = require("./SyncWaterfallHook");
exports.SyncLoopHook = require("./SyncLoopHook");
exports.AsyncParallelHook = require("./AsyncParallelHook");
exports.AsyncParallelBailHook = require("./AsyncParallelBailHook");
exports.AsyncSeriesHook = require("./AsyncSeriesHook");
exports.AsyncSeriesBailHook = require("./AsyncSeriesBailHook");
exports.AsyncSeriesWaterfallHook = require("./AsyncSeriesWaterfallHook");
exports.HookMap = require("./HookMap");
exports.MultiHook = require("./MultiHook");
注册事件回调
tapAsync和tapPromise 不能用于sync开头的hook,
执行顺序
在注册事件回调时,配置对象可以改变执行顺序
- before。其类型可以是数组也可以是一个字符串,传入的是注册事件回调的名称
- stage。其类型是数字,数字越小事件回调执行的时机越早
hook.tap({
name: 'first',
}, (params) => {
console.log('first')
})
hook.tap({
name: 'second',
before: 'first',
}, (params) => {
console.log('second')
})
hook.tap({
name: 'third',
stage: -1
}, (params) => {
console.log('third')
})
hook.call('tapable', 'siven')
//输出
third
second
first
事件触发
监听有三种方式,同样的,触发也有三种方式。
- call ,对应tap
- callAsync,对应tapAsync
- promise,对应tapPromise
call
hook.tap({
name: 'frist',
}, (params) => {
console.log('third')
})
hook.call('tapable', 'siven')
callAsync
hook.tapAsync("first", (name, callback) => {
console.log("first", name);
callback();
});
hook.tapAsync("second", (name, callback) => {
console.log("second", name);
callback();
});
hook.callAsync("callAsync", (error, result) => {
console.log("callAsync", error, result);
});
事件回调中的callback必须要执行,否则不会执行后续的事件回调和 callAsync 传入的回调。
这个callback经过了一层封装,其内部逻辑是:
if(_err1) {
_callback(_err1);
} else {
_next1();
}
_next1是下一个监听回调,对应本例子中second的回调。
_callback 是callAsync的回调函数。
如果 callback 执行时不传入值,就会继续执行后续的事件回调。
。如果传入错误信息,就会直接执行 _callback,不再执行后续的事件回调。
promise
promise 执行之后会返回一个 Promise 对象。在使用 tapPromise 注册事件回调时,事件回调必须返回一个 Promise 对象,否则会报错
hook.tapPromise("first", name => {
console.log("first", name);
return Promise.resolve("first");
});
hook.tapPromise("second", name => {
console.log("second", name);
return Promise.resolve("second");
});
const promise = hook.promise("promise");
promise.then(
value => {
console.log("value", value);
},
reason => {
console.log("reason", reason);
}
);
hook的其他参数
Interception
所有的hooks都能添加额外的interception参数
interface Tap {
name: string, // 标记每个 handler,必须有,
before: string | array, // 插入到指定的 handler 之前
type: string, // 类型:'sync', 'async', 'promise'
fn: Function, // handler
stage: number, // handler 顺序的优先级,默认为 0,越小的排在越前面执行
context: boolean // 内部是否维护 context 对象,这样在不同的 handler 就能共享这个对象
}
hook.tap({
name: 'first',
}, (params) => {
console.log('first')
})
hook.tap({
name: 'second',
before: 'first',
}, (params) => {
console.log('second')
})
hook.tap({
name: 'third',
stage: -1
}, (params) => {
console.log('third')
})
hook.call('tapable', 'siven')
//输出
third
second
first
context
插件和拦截器都可以往里面传一个上下文对象的参数,该对象可用于向后续插件和拦截器传递任意值。这个对象会变成回调函数的第一个参数。
const hook = new SyncHook(['name']);
hook.intercept({
// 启用 context
context: true,
register(tap) {
console.log('register', tap);
return tap;
},
call(...args) {
console.log('call', args);
},
loop(...args) {
console.log('loop', args);
},
tap(context, tap) {
context.hasContext = true;
console.log('tap', context, tap);
},
});
hook.tap(
{
name: 'first',
context: true,
},
(context, name) => {
// context: { hasContext: true }
console.log('first', context, name);
}
);
原理模拟实现
SyncHook
同步Hook不关心返回值。调用时将所有的监听函数执行一次。
class SyncHook{
constructor(){
this.subs = [];
}
tap(fn){
this.sub.push(fn);
}
call(...args){
this.subs.forEach(fn=>fn(...args));
}
}
SyncBailHook
如果监听函数返回值不为undefined,剩余监听函数被跳过。
class SyncBailHook{
constructor(){
this.subs = [];
}
tap(fn){
this.sub.push(fn);
}
call(...args){
for(let i=0; i< this.subs.length; i++){
const result = this.subs[i](...args);
if (result !== undefined){
break
}
}
}
SyncWaterHook
class SyncWaterHook{
constructor(){
this.subs = [];
}
tap(fn){
this.sub.push(fn);
}
call(...args){
let result = null;
for(let i = 0, l = this.hooks.length; i < l; i++) {
let hook = this.hooks[i];
result = i == 0 ? hook(...args): hook(result);
}
}
}
SyncLoopHook
返回值不等于undefined就循环执行。
class SyncLooHook{
constructor(){
this.subs = [];
}
tap(fn){
this.sub.push(fn);
}
call(...args){
let result;
do {
result = this.hook(...arguments);
} while (result!==undefined)
}
}
AsyncSeriesHook
class AsyncSeriesHook{
constructor() {
this.hooks = [];
}
tapAsync(name, fn) {
this.hooks.push(fn);
}
callAsync() {
var slef = this;
var args = Array.from(arguments);
let done = args.pop();
let idx = 0;
function next(err) {
if (err) return done(err);
let fn = slef.hooks[idx++];
fn ? fn(...args, next) : done();
}
next();
}
}
以下是tapable暴露的所有hooks类型。
注册事件回调
tapAsync和tapPromise 不能用于sync开头的hook,
执行顺序
在注册事件回调时,配置对象可以改变执行顺序
事件触发
监听有三种方式,同样的,触发也有三种方式。
call
callAsync
事件回调中的callback必须要执行,否则不会执行后续的事件回调和 callAsync 传入的回调。
这个callback经过了一层封装,其内部逻辑是:
_next1是下一个监听回调,对应本例子中second的回调。
_callback 是callAsync的回调函数。
如果 callback 执行时不传入值,就会继续执行后续的事件回调。
。如果传入错误信息,就会直接执行 _callback,不再执行后续的事件回调。
promise
promise 执行之后会返回一个 Promise 对象。在使用 tapPromise 注册事件回调时,事件回调必须返回一个 Promise 对象,否则会报错
hook的其他参数
Interception
所有的hooks都能添加额外的interception参数
context
插件和拦截器都可以往里面传一个上下文对象的参数,该对象可用于向后续插件和拦截器传递任意值。这个对象会变成回调函数的第一个参数。
原理模拟实现
SyncHook
同步Hook不关心返回值。调用时将所有的监听函数执行一次。
SyncBailHook
如果监听函数返回值不为undefined,剩余监听函数被跳过。
SyncWaterHook
SyncLoopHook
返回值不等于undefined就循环执行。
AsyncSeriesHook