webpack plugin
webpack完成的复杂炫酷的功能依赖于插件机制,webpack的插件机制依赖于核心的库, tapable
tapable是一个类似于nodejs的eventEmitter的库, 主要是控制钩子函数的发布喝定于,当时,tapable提供您的hook机制比较全面,分为同步和异步两个大类, 根据事件执行的终止条件的不同
const {SyncHook}= require('tapable')
const hook = new SyncHook(['arg1', 'arg2'])
hook.tap('a', function(arg1, arg2) {
console.log('ar')
})
hook.tap('b',function(arg1, arg2) {
console.log('b')
})
hook.call(1, 2)
BasicHook: 执行每一个,不关心函数的返回值,有SyncHook, AsyncParallelHook, AsyncSeriesHook
BailHook: 顺序执行hook,遇到第一个结果result!=undefined则返回,不在执行
SyncBailHook, AsyncSeriesBailHook, AsyncParallelBailHook
tapable => sync => SyncHook
=> SyncBailHook
=> SyncWaterfallHook
=> SyncLoopHook
=> Async => AsyncParallel => AsyncParallelHook
=> AsyncParallelBailHook
=> AsyncSeries => AsyncSeriesHook
=> AsyncParallelBailHook
=> AsyncSeriesLoopHook
=> AsyncSeriesWaterfallHook
每个Hook的用法
Basic: 不关心回调函数返回值,[SyncHook, AsyncParallelHook, AsyncSeriesHook]
Bail: 只要其中一个监听函数的返回值,不为undefined,则终止执行。 [SyncBailHook, AsyncParallelHook, AsyncSeriesBailHook]
waterfall: 前一个监听函数的返回值部位undefined,则作为下一个监听函数的第一个参数
[SyncWaterfallHook, AsyncSeriesWaterfallHook]
Loop: 如果有一个监听函数的返回值不为undefined, 则终止向下执行,从头开始执行,直到所有监听函数的返回值为undefined。[SyncLoopHook, AsyncSeriesLoopHook]~
```javascript
const {SyncHook} = require('tapable')
let hook = new SyncHook(['name'])
hook.tap('demo', function(params) {
console.log('demo', params)
})
hook.tap('demo2', function(params) {
console.log('demo2', params)
return true
})
hook.tap('demo3', function(params) {
console.log('demo3', params)
})
hook.call('hello SyncHook')
class SyncHook {
constructor(args = []) {
this._args = args
this.tasks = []
}
tap(name, task) {
this.tasks.push(task)
}
call(...args) {
const params = args.slice(0, this._args.length)
this.tasks.forEash(task => task(...params))
}
}
const {SyncBailHook} = require('tapable')
let hook = new SyncBailHook(['name'])
hook.tap('demo', function(params) {
console.log('demo', params)
})
hook.tap('demo2', function(params) {
console.log('demo2', params)
return true
})
hook.tap('demo3', function(params) {
console.log('demo3', params)
})
hook.call('hello SyncBailHook')
SyncBailHook钩子遇到回调函数中返回结果不为undefined的时候,则跳过下面所有的逻辑
class SyncBailHook {
constructor(args) {
this._args = _args
this.tasks = []
}
tap(name, task) {
this.tasks.push(task)
}
call() {
const args = Array.from(arguments).slice(0, this._args.length)
for(let i = 0; i < this.tasks.length; i++) {
if(result !== undefiend) {
break
}
}
}
}
SyncWaterfallHook
const {SyncWaterfallHook} = require('tapable')
const hook = new SyncWaterfallHook(['author', 'age'])
hook.tap('测试1', (params1, params2) => {
console.log('测试1接受的参数', params1, params2)
})
hook.tap('测试2', (params1, params2) => {
console.log('测试2接受的参数', params1, params2)
})
hook.tap('测试3',(params1, params2) => {
console.log('测试3接受的参数', params1, params2)
})
hook.call('嘎嘎嘎', '99')
syncWaterfallHook是一个同步的,瀑布类型的hook,瀑布类型的钩子就是如果前一个事件的函数结果部位undefined,则result会作为后一个事件函数的第一个参数,也就是上一个函数的执行结果会成为下一个函数的参数
const {SyncLoopHook} = require('tapable')
const hook = new SyncLoopHook([])
let count = 5
hook.tap('测试1', () => {
console.log("测试1里面的count", count)
if([1, 2, 3].includes(count) {
return undefiend
} else {
count--
return "123"
})
})
hook.tap('测试2', () => {
console.log("测试2里面的count", count)
if([1, 2].includes(count) {
return undefiend
} else {
count--
return "123"
})
})
hook.tap("测试3", () => {
console.log("测试3里面的count",count)
if([1].includes(count) {
return undefiend
} else {
count--
return "123"
})
})
hook.call()
syncLoophook=>asyncparallelHook是一个异步并行,基本类型的Hook, 与同步不同的地方在于
- 会同时开启多个异步任务,而且需要通过tapSaync
const {AsyncParallelHook} = require("tapable")
const hook = new AsyncParallelHook(['author', 'age'])
console.log('time')
hook.tapAsync('测试1', (params1, params2) {
setTimeout(() => {
console.log('测试1接受的参数', params1, params2)
callback()
}, 2000)
})
hook.tapAsync('测试2', (params1, params2, callback) {
console.log("测试2接受的参数", params1, params2)
callback()
})
hook.tapAsync('测试3',(params1, params2,callback) => {
console.log("测试3接受的参数", params1, params2)
callback()
})
hook.callAsync('嘎嘎嘎', "99",(err, result) => {
console.log("这个是成功的回调",err, result)
console.timeEnd("time")
})
AsyncParallelBailHook是一个异步并行,保险类型的hook,只要其中一个有返回值, 就会执行callAsync中的回调
const {AsyncParallelHook} = require('tapable')
const hook = new AsyncParallelBailHook(['author', 'age'])
console.time('time')
hook.tapAsync('测试1', (params1, params2, callback) => {
console.log('测试1接受的参数',params1, params2)
setTimeout(() => {
callback()
},1000)
})
hook.tapAsync("测试2", (params1,params2,callback) => {
console.log('测试2接受的参数',params1, params2)
setTimeout(() => {
callback(null, '测试2有返回值')
}, 2000)
})
hook.tapAsync('测试3', (params1, params2, callback) => {
console.log('测试3接受的参数',params1, params2)
setTimeout(()=> {
callback(null, '测试3有返回值')
}, 3000)
})
hook.callAsync("嘎嘎嘎", "99", (err, result) => {
//等全部都完成了才会走到这里来
console.log("这是成功后的回调", result);
console.timeEnd("time");
});
AsyncSeriesHook是一个异步的,串行的类型的hook,只有前面的执行完成了,后面的才会执行
const {AsyncSeriesHook} = require('tapable')
const hook = new AsyncSeriesHook(['author', 'age'])
console.time('time')
hook.tapAsync('测试1', (params1, params2, callback) => {
console.log('测试1接受的参数', params1, params2)
setTimeout(() => {
callback()
}, 1000)
})
hook.tapAsync('测试2', (params1, params2, callback) => {
console.log('测试2接受的参数', params1, params2)
setTimeout(() => {
callback()
}, 2000)
})
hook.tapAsync('测试3', (params1, params2, callback) => {
console.log('测试3接受的参数', params1, params2)
setTimeout(() => {
callback()
}, 3000)
})
console.timeEnd()
AsyncSeriesBailHook 是一个异步串行,保险类型的hook,在串行的执行过程中,只要其中一个有返回值,后面就不会执行
const {AsyncSeriesBailHook} = require('tapable')
const hook = new AsyncParallelHook(['author', 'age'])
hook.tapAsync('测试1',(params1, params2, callback) => {
console.log('测试1接受的参数',params1, params2)
setTimeout(() => {
callback()
}, 1000)
})
hook.tapAsync('测试2',(params1, params2,callback) {
console.log('测试2接受的参数',params1, params2)
setTimeout(() => {
callback()
}, 2000)
})
hook.tapAsync('测试3',(params1, params2,callback) {
console.log('测试3接受的参数',params1, params2)
setTimeout(() => {
callback()
}, 3000)
})
hook.callAsync("嘎嘎嘎", "99", (err, result) => {
//等全部都完成了才会走到这里来
console.log("这是成功后的回调", result);
console.timeEnd("time");
});
AsyncSeriesWaterfallHook是一个异步串行,瀑布类型的hook, 如果前一个事件函数的结果result!==undefined,则result会作为后一个事件函数的第一个参数,也就是上一个函数的执行结果会称为下一个函数的参数
const {AsyncSeriesWaterfallHook} = require('tapable')
const hook = new AsyncSeriesWaterfallHook(['author', 'age'])
console.time('time')
hook.tapAsync("测试1", (params1, params2, callback) => {
console.log('测试1接受的参数', params1, params2)
setTimeout(() => {
callback(() => {
callback(null, '2')
}, 1000)
})
})
hook.tapAsync("测试2", (params1, params2, callback) => {
console.log('测试2接受的参数', params1, params2)
setTimeout(() => {
callback(null, '3')
},2000)
})
hook.tapAsync("测试3", (param1, param2, callback) => {
console.log("测试3接收的参数:", param1, param2);
setTimeout(() => {
callback(null, "4");
}, 3000);
});
hook.callAsync("嘎嘎嘎", "99", (err, result) => {
//等全部都完成了才会走到这里来
console.log("这是成功后的回调", err, result);
console.timeEnd("time");
});
Tapable拦截器
Tapable提供的所有的hook都支持注入Interception,它和Axios的拦截器的效果非常类似,可以通过拦截器对整个Tapable发布/订阅流程进行监听,从而触发对应的逻辑
const hook = new SyncHook(['arg1', 'arg2', 'arg3'])
hook.intercept({
// 每次调用hook实例的tap,方法注册回调函数的时候,就会调用该方法
// 并且接受tap作为参数,还可以对tap进行修改
register: (tapInfo) => {
console.log(`${tapInfo.name} is doing its job`)
return tapInfo
},
// 通过hook实例对象上的call方法时候触发拦截器
call: (arg1, arg2, arg3) => {
console.log('starting to calculate routes')
}
// 在调用被注册的每一个事件函数之前执行
tap: (tap) => {
console.log(tap, 'tap'))
}
// loop类型钩子中,每个事件函数被调用前触发拦截器方法
loop:(...args) => {
console.log(args, 'loop')
}
})