tapable详解
tapable是webpack内部使用的一个流程管理工具,主要用来串联插件,完善事件流执行。
1.安装tapable
yarn add tapable
2. 常用hooks
import {
SyncHook,
SyncBailHook,
SyncWaterfallHook,
SyncLoopHook,
AsyncParallelHook,
AsyncParallelBailHook,
AsyncSeriesHook,
AsyncSeriesBailHook,
AsyncSeriesWaterfallHook
} form 'tapable';
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gBgy8rW0-1611562755204)(D:\download\tapable.png)]
tapable通过
tap
注册一个事件,通过call
执行该钩子注册的所有事件。tapable的每个hooks都
tap
一个或多个事件。
tapAsync/callAsync
、tapPromise/Promise
用于注册同步执行的异步事件,callAsync
用在并行执行的异步钩子完成后再执行该函数。
用法示例:
new SyncHook([arg1,arg2,...])
hooks接收一个数组参数,参数为执行回调事件所需的参数名。
-
call:
(...args) => void
当你的钩子触发之前,(就是call()之前),就会触发这个函数,你可以访问钩子的参数.多个钩子执行一次 -
tap:
(tap: Tap) => void
每个钩子执行之前(多个钩子执行多个),就会触发这个函数 -
loop:
(...args) => void
这个会为你的每一个循环钩子(LoopHook, 就是类型到Loop的)触发,具体什么时候没说 -
register:
(tap: Tap) => Tap | undefined
每添加一个Tap
都会触发 你interceptor上的register,你下一个拦截器的register 函数得到的参数 取决于你上一个register返回的值,所以你最好返回一个 tap 钩子.
2.1 SyncHook
同步串行,在触发事件之后,会依次执行注册的所有事件处理函数。其原理是将监听(订阅)的函数存放到一个数组中, 发布时遍历数组中的监听函数并且将发布时的 arguments传递给监听函数。
不关心返回值,从上到下依次执行注册事件。
const hook = new SyncHook(['name', 'sex'])
/*
tap(options,function):
options是事件描述,可以为一个字符串,也可以为一个对象,为对象时必须包含name属性,描述该插件名称。
function:回调函数
*/
// 打印我的名字
hook.tap('printName', (name) => {
console.log('my name is ' + name);
})
hook.tap('printSex', (name, sex) => {
console.log('I’m a ' + sex);
})
// call(arg1,arg2,...)
hook.call('张三', 'man');
执行结果:
my name is 张三
I’m a man
Basic Hook:依次执行注册事件,无法中断。
2.2 SyncBailHook
同步串行,当注册事件无返回值,或者返回undefined
时继续执行之后的注册事件,否则中断执行。
const hook = new SyncBailHook(['name', 'sex', 'age'])
/*
tap(options,function):
options是事件描述,可以为一个字符串,也可以为一个对象,为对象时必须包含name属性,描述该插件名称。
function:回调函数
*/
// 打印我的名字
hook.tap('printName', (name) => {
const nameStr = 'my name is ' + name;
console.log(nameStr);
return;
})
hook.tap('printSex', (name, sex) => {
const sexStr = 'I’m a ' + sex;
console.log(sexStr);
return sexStr;
})
hook.tap('printAge', (name, sex, age) => {
const ageStr = 'I’m ' + age + ' years old';
console.log(ageStr);
})
// call(arg1,arg2,...)
hook.call('张三', 'man', 29);
执行结果:
my name is 张三
I’m a man
Bail Hook:运行提前退出执行函数,当存在返回值不为undefined时,则不再进行之后的事件执行。
2.3 SyncWaterfallHook
同步瀑布流,依次执行注册的所有事件,并将上一个事件的执行结果作为下一个事件的入参。
const hook = new SyncWaterfallHook(['name', 'sex', 'age'])
/*
tap(options,function):
options是事件描述,可以为一个字符串,也可以为一个对象,为对象时必须包含name属性,描述该插件名称。
function:回调函数
*/
// 打印我的名字
hook.tap('printName', (name, sex, age) => {
const nameStr = 'my name is ' + name;
console.log(nameStr);
return { sex, age };
})
hook.tap('printSex', (data) => {
const sexStr = 'I’m a ' + data.sex;
console.log(sexStr);
return data.age;
})
hook.tap('printAge', (age) => {
const ageStr = 'I’m ' + age + ' years old';
console.log(ageStr);
})
// call(arg1,arg2,...)
hook.call('张三', 'man', 29);
执行结果:
my name is 张三
I’m a man
I’m 29 years old
Waterfall Hook:接收上一个注册事件的返回值作为下一个注册事件的参数。
2.4 SyncLoopHook
同步循环执行,当注册事件返回值非undefined时,循环执行该注册事件,直至返回undefined时,才继续执行下一个事件。
const hook = new SyncLoopHook(['name', 'sex', 'age'])
/*
tap(options,function):
options是事件描述,可以为一个字符串,也可以为一个对象,为对象时必须包含name属性,描述该插件名称。
function:回调函数
*/
// 打印我的名字
let num = 0;
hook.tap('printName', (name) => {
const nameStr = 'my name is ' + name;
console.log(nameStr);
num++;
return num === 3 ? undefined : false;
})
hook.tap('printSex', (name, sex) => {
const sexStr = 'I’m a ' + sex;
console.log(sexStr);
return;
})
hook.tap('printAge', (name, sex, age) => {
const ageStr = 'I’m ' + age + ' years old';
console.log(ageStr);
})
// call(arg1,arg2,...)
hook.call('张三', 'man', 29);
执行结果:
my name is 张三
my name is 张三
my name is 张三
I’m a man
I’m 29 years old
Loop Hook:循环执行注册事件,直至返回undefined才进入下一个注册事件。
2.5 AsyncParallelHook
异步并行执行,当所有注册事件都执行完成后,才执行callAsync
或者promise
。
const hook = new AsyncParallelHook(['name', 'sex', 'age'])
/*
tap(options,function):
options是事件描述,可以为一个字符串,也可以为一个对象,为对象时必须包含name属性,描述该插件名称。
function:回调函数
*/
// 打印我的名字
let num = 0;
console.time('time')
hook.tapAsync('printName', (name, sex, age, next) => {
setTimeout(() => {
const nameStr = 'my name is ' + name;
console.log(nameStr);
next();
}, 1000)
})
hook.tapAsync('printSex', (name, sex, age, next) => {
setTimeout(() => {
const sexStr = 'I’m a ' + sex;
console.log(sexStr);
next();
}, 2000)
})
hook.tapAsync('printAge', (name, sex, age, next) => {
setTimeout(() => {
const ageStr = 'I’m ' + age + ' years old';
console.log(ageStr);
next();
}, 3000)
})
// call(arg1,arg2,...)
hook.callAsync('张三', 'man', 29,()=>{
console.log('执行完成!');
console.timeEnd('time');
});
执行结果:
my name is 张三
I’m a man
I’m 29 years old
执行完成!
time: 3002.875732421875 ms
callAsync:最后一个参数接收一个回调函数,当所有的注册事件都执行完成后,才会执行该回调。
tapAsync:除了hook声明的参数,它还接收一个next()回调,当存在next时,当所有注册事件执行完成后则执行next回调。
Parallel Hook:异步并行执行,所有的异步事件都执行完成后,才执行callAsync/promise定义的回调。
2.6 AsyncSeriesHook
异步串行执行,与AsyncParallelHook
类似,所有注册事件执行完成后才会执行callAsync/promise
定义的回调。
const hook = new AsyncSeriesHook(['name', 'sex', 'age'])
/*
tap(options,function):
options是事件描述,可以为一个字符串,也可以为一个对象,为对象时必须包含name属性,描述该插件名称。
function:回调函数
*/
// 打印我的名字
let num = 0;
console.time('time')
hook.tapPromise('printName', (name, sex, age) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
const nameStr = 'my name is ' + name;
console.log(nameStr);
resolve('printName');
}, 1000)
})
})
hook.tapPromise('printSex', (name, sex, age) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
const sexStr = 'I’m a ' + sex;
console.log(sexStr);
resolve('printSex')
}, 2000)
})
})
hook.tapPromise('printAge', (name, sex, age) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
const ageStr = 'I’m ' + age + ' years old';
console.log(ageStr);
resolve('printAge')
}, 3000)
})
})
// call(arg1,arg2,...)
hook.promise('张三', 'man', 29).then(res => {
console.log(res);
console.timeEnd('time');
});
执行结果:
my name is 张三
I’m a man
I’m 29 years old
undefined
time: 6005.301025390625 ms
2.7 其他
- AsyncParallelBailHook:异步并行且运行中断。
- AsyncSeriesBailHook :异步串行且运行中断。
- AsyncSeriesWaterfallHook:异步串行瀑布流,上一个事件的结果作为下一个事件的参数。
3.拦截器(interception)
所有的钩子函数都有额外的拦截器。
- call:执行
call/callAsync/promise
时触发。 - register:定义
tap/tapAsync/tapPromise
时触发。 - tap:执行
tap/tapAsync/tapPromise
定义的内容时触发。 - loop:执行
SyncLoopHook
注册的事件时触发。 - error: 执行报错时触发。
- result: 存在返回结果时触发,主要针对
Waterfall Hook
/Bail Hook
,在call之前,tap之后触发。 - done: 执行
call/callAsync/promise
完成后触发。
const hook = new AsyncSeriesHook(['name', 'sex', 'age'])
hook.intercept({
call: (name, sex, age) => {
console.log('执行拦截器');
},
register: (name) => {
console.log('定义钩子函数时的拦截器');
},
tap: () => {
console.log('执行钩子函数时的拦截器');
},
loop:()=>{
console.log('循环事件函数执行时的拦截器');
}
})
// 打印我的名字
let num = 0;
console.time('time')
hook.tapPromise('printName', (name, sex, age) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
const nameStr = 'my name is ' + name;
console.log(nameStr);
resolve('printName');
}, 1000)
})
})
hook.tapPromise('printSex', (name, sex, age) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
const sexStr = 'I’m a ' + sex;
console.log(sexStr);
resolve('printSex')
}, 2000)
})
})
hook.tapPromise('printAge', (name, sex, age) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
const ageStr = 'I’m ' + age + ' years old';
console.log(ageStr);
resolve('printAge')
}, 3000)
})
})
// call(arg1,arg2,...)
hook.promise('张三', 'man', 29).then(res => {
console.log(res);
console.timeEnd('time');
});
执行结果:
定义钩子函数时的拦截器
定义钩子函数时的拦截器
定义钩子函数时的拦截器
执行拦截器
执行钩子函数时的拦截器
my name is 张三
执行钩子函数时的拦截器
I’m a man
执行钩子函数时的拦截器
I’m 29 years old
undefined
time: 6005.77099609375 ms
4.HookMap
一个 HookMap是一个Hooks映射的帮助类。实际就是一个hook的key-value数组。
const hookMap = new HookMap(key => new AsyncSeriesWaterfallHook(['name', 'sex', 'age']));
hookMap.tapPromise('myPlugin', 'printName', (name, sex, age) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
const nameStr = 'my name is ' + name;
console.log(nameStr);
resolve('printName');
}, 1000)
})
});
const hook = hookMap.get('myPlugin');
// call(arg1,arg2,...)
hook.promise('张三', 'man', 29).then(res => {
console.log(res);
});
5.MultiHook
把其他的Hook 重定向(转化)成为一个 MultiHook
import { MultiHook } from "tapable";
const hookA = new SyncHook(['name']);
const hookB = new SyncBailHook(['age']);
const allHooks = new MultiHook([hookA, hookB]);
.log(nameStr);
resolve(‘printName’);
}, 1000)
})
});
const hook = hookMap.get(‘myPlugin’);
// call(arg1,arg2,…)
hook.promise(‘张三’, ‘man’, 29).then(res => {
console.log(res);
});
## 5.MultiHook
把其他的Hook 重定向(转化)成为一个 MultiHook
import { MultiHook } from “tapable”;
const hookA = new SyncHook([‘name’]);
const hookB = new SyncBailHook([‘age’]);
const allHooks = new MultiHook([hookA, hookB]);