在要学习自己写wepack 插件前,有必要了解一下tapable
什么是tapable
tapable 这个小型 library 是 webpack 的一个核心工具,但也可用于其他地方,以提供类似的插件接口。webpack 中许多对象扩展自 Tapable 类。
基本概念
tapable 中主要提供了同步与异步两种钩子;
其中异步钩子 包括 并行异步钩子和串行钩子。
所有tapable实际提供了三种钩子
- 同步钩子
- 异步并行钩子
- 异步串行钩子
1. 同步钩子
SyncHook,
SyncBailHook,
SyncWaterfallHook,
SyncLoopHook
2. 异步并行钩子 (并行执行的异步钩子,当注册的所有异步回调都并行执行完毕之后再执行 callAsync 或者 promise 中的函数)
AsyncParallelHook,
AsyncParallelBailHook
3. 异步串行钩子 (顺序的执行异步函数,当注册的所有异步回调都并行执行完毕之后再执行 callAsync 或者 promise 中的函数)
AsyncSeriesHook,
AsyncSeriesBailHook,
AsyncSeriesWaterfallHook,
AsyncSeriesLoopHook
钩子hook类型解析
-
基本钩子Hook
:简单的调用每个tap穿进去的函数 -
BailHook,
: 当某个tap传进来的函数有返回值的时候,停止往后执行 -
WaterfallHook
: 上一个tap传的函数的返回值作为下一个tap传的函数的入参 -
LoopHook
:类似于while循环,tap传进来的函数没有返回值的时候停止执行
AsyncParallelBailHook:执行过程中注册的回调返回非 undefined 时就会直接执行 callAsync 或者 promise 中的函数(由于并行执行的原因,注册的其他回调依然会执行)。 AsyncSeriesBailHook:执行过程中注册的回调返回非 undefined 时就会直接执行 callAsync 或者 promise 中的函数,并且注册的后续回调都不会执行。
钩子的注册方式(类似于绑定click事件)
tap : 使用同步钩子
tapASync: 使用callback回调的异步钩子
tapPromise:使用promise回调的异步钩子
异步钩子也可以使用tap,但是没必要。同步钩子一般使用tap。异步钩子使用tapAsync或者tapPromise
钩子的调用方式 (类似于触发click事件)
call: 调用注册的同步钩子
callAsync: 调用注册有callback回调的异步钩子
promise:调用注册有promise回调的异步钩子
栗子
注意: 所有的Hook构造函数都采用一个可选参数,该参数是字符串形式的参数名称的列表
const hook = new SyncHook(["arg1", "arg2", "arg3"]);
https://github.com/webpack/tapable
hook.tap(name, callback)
syncHook
const {
SyncHook,
SyncBailHook,
SyncWaterfallHook,
SyncLoopHook
} = require('tapable');
const Synchook1 = new SyncHook(['zs', 'll']);
// hook.tap(name, callback)
Synchook1.tap('plugin1', (name,age)=>{
console.log('plugin1', name,age);
return name
})
Synchook1.tap('plugin2', (name,age)=>{
console.log('plugin2', name,age);
})
Synchook1.call('ww',13)
/**
* plugin1 ww 13
* plugin2 ww 13
*/
SyncBailHook (有返回终止执行)
const SyncBailHook1 = new SyncBailHook(['name', 'age'])
SyncBailHook1.tap('a', (name,age)=>{
console.log('a',name,age);
// return undefined (不返回或者返回undefined会继续往下)
})
SyncBailHook1.tap('b', (name,age)=>{
console.log('b',name,age);
return name
})
SyncBailHook1.tap('c', (name,age)=>{
console.log('c',name,age);
})
SyncBailHook1.call('xunwukong', 500)
/**
* a xunwukong 500
* b xunwukong 500
* */
SyncWaterfallHook (返回值给下一个函数用)
const SyncWaterfallHook1 = new SyncWaterfallHook(['name'])
SyncWaterfallHook1.tap('aa', (name)=>{
console.log('aa', name);
return 'aa'+name
})
SyncWaterfallHook1.tap('bb', (name)=>{
console.log('bb', name);
// return 'bb'+name (如果有返回 下面cc 的name就是这里的返回)
})
SyncWaterfallHook1.tap('cc', (name)=>{
console.log('cc', name);
})
SyncWaterfallHook1.call('zbj')
/**
* aa zbj
* bb aazbj
* cc aazbj
* */
SyncLoopHook (返回undefined停止执行)
const SyncLoopHook1 = new SyncLoopHook(['name','age'])
let num =1
SyncLoopHook1.tap('aaaa', (name)=>{
console.log('aaaa', name);
if(num>3){
console.log('end', num, name);
return;
}
num++
return true
})
SyncLoopHook1.call('swj')
/**
aaaa swj
aaaa swj
aaaa swj
aaaa swj
end 4 swj
*/
webpack-tapable 类似钩子的封装
const {
AsyncSeriesHook,
AsyncSeriesWaterfallHook,
} = require('tapable')
class Modal {
constructor(){
this.hooks = {
asyncSeriesHook111: new AsyncSeriesHook(['name']),
promiseSeriesWaterfallHook222: new AsyncSeriesWaterfallHook(['name'])
}
}
// tapAsync 触发
callAsyncHookxxxx(name, callback){
this.hooks.asyncSeriesHook111.callAsync(name, res=>{
if(res){
return callback(res)
}
return callback(null)
})
}
// tapPromise 触发
callPromiseHookxxxx(name){
return this.hooks.promiseSeriesWaterfallHook222.promise(name).then(res=>{
return res
})
}
}
// 实例化
const modal = new Modal()
// 注册
modal.hooks.asyncSeriesHook111.tapAsync('plugin1', (name, cb)=>{
setTimeout(() => {
console.log('aaa', name);
cb()
}, 3000);
})
modal.hooks.asyncSeriesHook111.tapAsync('plugin2', (name, cb)=>{
setTimeout(() => {
console.log('bbb', name);
cb('send___bbb')
}, 2000);
})
modal.hooks.asyncSeriesHook111.tapAsync('plugin3', (name, cb)=>{
setTimeout(() => {
console.log('ccc', name);
cb()
}, 1000);
})
// 执行
modal.callAsyncHookxxxx('heheda', res=>{
console.log('end',res);
})
// 结果
// aaa heheda
// bbb heheda
// end send___bbb