内容简介:为什么我们要学tapable,因为....webpack源码里面都是用的tapable来实现钩子挂载的,作为一个有点追求的code,webpack怎么能只满足于用呢?当然是要去看源码,写loader,plugin啦.在这之前,要是不清楚tapable的用法,源码那是更不用看了,看不懂.....所以,今天来讲一下tapable吧webpack本质上是一种事件流的机制,他的工作流程就是将各个插件串联起来,而实现这一切的核心就是Tapable,webpack中最核心的负责编译的Compiler和负责创建的bun
为什么我们要学tapable,因为....webpack源码里面都是用的tapable来实现钩子挂载的,作为一个有点追求的code,webpack怎么能只满足于用呢?当然是要去看源码,写loader,plugin啦.在这之前,要是不清楚tapable的用法,源码那是更不用看了,看不懂.....所以,今天来讲一下tapable吧
1. tapable
webpack本质上是一种事件流的机制,他的工作流程就是将各个插件串联起来,而实现这一切的核心就是Tapable,webpack中最核心的负责编译的Compiler和负责创建的bundles的Compilation都是Tapable的实例
tapable创建实例时传递的参数对于程序运行并没有任何作用,只是给源码阅读者提供帮助
同样的,在使用tap*注册监听时,传递的第一个参数,也只是一个标识,并不会在程序运行中产生任何影响。而第二个参数则是回调函数
2.tapable的用法
const {
SyncHook,
SyncBailHook,
SyncWaterHook,
SyncLoopHook
AsyncParallelHook,
AsyncParallelBailHook,
AsyncSeriesHook,
AsyncSeriesBailHook,
AsyncSeriesWaterfallHook
} = require("tapable");
复制代码
| 序号 | 钩子名称 | 执行方式 | 使用要点 |
|---|---|---|---|
| 1 | SyncHook | 同步串行 | 不关心监听函数的返回值 |
| 2 | SyncBailHook | 同步串行 | 只要监听函数中有一个函数的返回值不为null,则跳过剩余逻辑 |
| 3 | SyncWaterfallHook | 同步串行 | 上一个监听函数的返回值将作为参数传递给下一个监听函数 |
| 4 | SyncLoopHook | 同步串行 | 当监听函数被触发的时候,如果该监听函数返回true时则这个监听函数会反复执行,如果返回 undefined 则表示退出循环 |
| 5 | AsyncParallelHook | 异步并行 | 不关心监听函数的返回值 |
| 6 | AsyncParallelBailHook | 异步并行 | 只要监听函数的返回值不为 null,就会忽略后面的监听函数执行,直接跳跃到callAsync等触发函数绑定的回调函数,然后执行这个被绑定的回调函数 |
| 7 | AsyncSeriesHook | 异步串行 | 不关心callback()的参数 |
| 8 | AsyncSeriesBailHook | 异步串行 | callback()的参数不为null,就会直接执行callAsync等触发函数绑定的回调函数 |
| 9 | AsyncSeriesWaterfallHook | 异步串行 | 上一个监听函数的中的callback(err, data)的第二个参数,可以作为下一个监听函数的参数 |
3. Sync*类型的钩子
- 注册在该钩子下面的插件的执行顺序都是顺序执行
- 只能使用tap注册,不能使用tapPromise和tapAsync注册
3.1 SyncHook
串行同步执行,不关心返回值 在SyncHook的实例上注册了tap之后,只要实例调用了call方法,那么这些tap的回掉函数一定会顺序执行一遍
let queue = new SyncHook(['没任何作用的参数']);
queue.tap(1,(name,age)=>{
console.log(name,age)
})
queue.tap(2,(name,age)=>{
console.log(name,age)
})
queue.tap(3,(name,age)=>{
console.log(name,age)
})
queue.call('bearbao',8)
// 输出结果
// 'bearbao' 8
// 'bearbao' 8
// 'bearbao' 8
复制代码
3.1.1 SyncHook实现
class SyncHook {
constructor(){
this.listeners = [];
}
tap(formal,listener){
this.listeners.push(listener)
}
call(...args){
this.listeners.forEach(l=>l(...args))
}
}
复制代码
3.2 SyncBailHook
串行同步执行,有一个返回值不为null则跳过剩下的逻辑
let queue = new SyncBailHook(['name'])
queue.tap(1,name=>{
console.log(name)
})
queue.tap(1,name=>{
console.log(name)
return '1'
})
queue.tap(1,name=>{
console.log(name)
})
queue.call('bearbao')
// 输出结果,只执行前面两个回调,第三个不执行
// bearbao
// bearbao
复制代码
实现
class SyncBailHook {
constructor(){
this.listeners = [];
}
tap(formal,listener){
this.listeners.push(listener)
}
call(...args){
for(let i=0;i<this.listeners.length;i++){
if(this.listeners[i]()) break;
}
}
}
复制代码
3.3 SyncWaterHook
串行同步执行,第一个注册的回调函数会接收call传进来的所有参数,之后的每个回调函数只接收到一个参数,就是上一个回调函数的返回值.
let queue = new SyncWaterHook(['name','age']);
queue.tap(1,(name,age)=>{
console.log(name,age)
return 1
})
queue.tap(2,(ret)=>{
console.log(ret)
return 2
})
queue.tap(3,(ret)=>{
console.log(ret)
return 3
})
queue.call('bearbao', 3)
// 输出结果
// bearbao 3
// 1
// 2
复制代码
SyncWaterHook 实现. SyncWaterHook这个方法很像redux中的compose方法,都是将一个函数的返回值作为参数传递给下一个函数.
对下面实现的call方法如果有疑惑,看不大懂的同学可以移步我之前对于compose函数的解读,里面有详细的介绍,这里就不多加赘述了
class SyncWaterHook{
constructor(){
this.listeners = [];
}
tap(formal,listener){
this.listener.push(listener);
}
call(...args){
this.listeners.reduce((a,b)=>(...args)=>a(b(...args)))
}
}
复制代码
3.4 SyncLoopHook
串行同步执行, 监听函数返回true表示继续循环,返回undefined表示循环结束
let queue = new SyncLoopHook;
let index = 0;
queue.tap(1,_=>{
index++
if(index<3){
console.log(index);
return true
}
})
queue.call();
// 输出结果
// 1
// 2
复制代码
SyncLoopHook实现
class SyncLoopHook{
constructor() {
this.tasks=[];
}
tap(name,task) {
this.tasks.push(task);
}
call(...args) {
this.tasks.forEach(task => {
let ret=true;
do {
ret = task(...args);
}while(ret)
});
}
}
复制代码
4. Async*类型的钩子
- 支持tap、tapPromise、tapAsync注册
- 每次都是调用tap、tapSync、tapPromise注册不同类型的插件钩子,通过调用call、callAsync 、promise方式调用。其实调用的时候为了按照一定的执行策略执行,调用compile方法快速编译出一个方法来执行这些插件。
4.1 AsyncParallel
异步并行执行
4.1.1 AsyncParallelHook
不关心监听函数的返回值.
有三种注册/发布的模式,如下
| 异步订阅 | 调用方法 |
|---|---|
| tap | callAsync |
| tapAsync | callAsync |
| tapPromise | promise |
- 通过tap来使用
触发函数的参数,出了最后一个参数是异步监听回调函数执行完成之后的回调,其他的参数都是传递给回调函数的参数
let queue = new AsyncParallelHook(['name']);
console.time('cost');
queue.tap('1',function(name){
console.log(name,1);
});
queue.tap('2',function(name){
console.log(name,2);
});
queue.tap('3',function(name){
console.log(name,3);
});
queue.callAsync('bearbao',err=>{
console.log(err);
console.timeEnd('cost');
});
// 执行结果
/*
bearbao 1
bearbao 2
bearbao 3
cost: 4.720ms
*/
复制代码
实现
class AsyncParallelHook {
constructor(){
this.listeners = [];
}
tap(name,listener){
this.listeners.push(listener);
}
callAsync(){
this.listeners.forEach(listener=>listener(...arguments));
Array.from(arguments).pop()();
}
}
复制代码
- 通过tapAsync来注册
注意,这里有个特殊的地方,如何确认某个回调执行完了呢?,每个监听回调的最后一个参数是一个回调函数,当执行callback之后,会认为当前函数执行完毕
let queue = new AsyncParallelHook(['name']);
console.time('cost');
queue.tapAsync('1',function(name,callback){
setTimeout(function(){
console.log(name, 1);
callback();
},1000)
});
queue.tapAsync('2',function(name,callback){
setTimeout(function(){
console.log(name, 2);
callback();
},2000)
});
queue.tapAsync('3',function(name,callback){
setTimeout(function(){
console.log(name, 3);
callback();
},3000)
});
queue.callAsync('bearbao',err=>{
console.log(err);
console.timeEnd('cost');
});
// 输出结果
/*
bearbao 1
bearbao 2
bearbao 3
cost: 3000.448974609375ms
*/
复制代码
实现
class AsyncParallelHook {
constructor(){
this.listeners = [];
}
tapAsync(name,listener){
this.listeners.push(listener);
}
callAsync(...arg){
let callback = arg.pop();
let i = 0;
let done = ()=>{
if(++i==this.listeners.length){
callback()
}
}
this.listeners.forEach(listener=>listener(...arg,done));
}
}
复制代码
- 使用tapPromise
使用tapPromise注册监听时,每个回调函数的返回值必须是一个Promise的实例
let queue = new AsyncParallelHook(['name']);
console.time('cost');
queue.tapPromise('1',function(name){
return new Promise(function(resolve,reject){
setTimeout(function(){
console.log(1);
resolve();
},1000)
});
});
queue.tapPromise('2',function(name){
return new Promise(function(resolve,reject){
setTimeout(function(){
console.log(2);
resolve();
},2000)
});
});
queue.tapPromise('3',function(name){
return new Promise(function(resolve,reject){
setTimeout(function(){
console.log(3);
resolve();
},3000)
});
});
queue.promise('bearbao').then(()=>{
console.timeEnd('cost');
})
// 执行记过
/*
1
2
3
cost: 3000.448974609375ms
*/
复制代码
实现
class AsyncParallelHook {
constructor(){
this.listeners = [];
}
tapPromise(name,listener){
this.listeners.push(listener);
}
promise(...arg){
let i = 0;
return Promise.all(this.listeners.map(l=>l(arg)))
}
}
复制代码
5. 好困好困
一不小心又到1点了,为了能够获得长寿成就,今天就先写到这里吧,后续几个方法,过两天再更新上来
结语
如果觉得还可以,能在诸君的编码之路上带来一点帮助,请点赞鼓励一下,谢谢!
以上所述就是小编给大家介绍的《webpack怎么能只是会用呢,核心中的核心tapable了解下?》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
算法统治世界——智能经济的隐形秩序
徐恪、李沁 / 清华大学出版社有限公司 / 2017-11-15 / CNY 69.00
今天,互联网已经彻底改变了经济系统的运行方式,经济增长的决定性要素已经从物质资料的增加转变成为信息的增长。但是,只有信息的快速增长是不够的,这些增长的信息还必须是“有序”的。只有“有序”才能使信息具有价值,能够为人所用,能够指导我们实现商业的新路径。这种包含在信息里的隐形秩序才是今天信息世界的真正价值所在。经济系统的运行确实是纷繁复杂的,但因为算法的存在,这一切变得有律可循,算法也成为新经济系统里......一起来看看 《算法统治世界——智能经济的隐形秩序》 这本书的介绍吧!