webpack系列文章
Tapable和webpack
Tapable是基于发布订阅模式实现的一个类库,提供了许多Hook类,可创建许多钩子。在这些钩子里面调用内置或者用户在webpack.config.js中使用的插件,webpack在编译打包代码的各个环节会使用到。
Tapable中Hook分类
- Taptable:
- Sync*:
- SyncHook
- SyncBailHook
- SyncWaterfallHook
- SyncLoopHook
- Async*:
- AsyncParallelHook
- AsyncParallelBailHook
- AsyncSeriesHook
- AsyncSeriesBailHook
- AsyncSeriesWaterfallHook
- Sync*:
Sync*型Hook
Sync*型的Hook里面的Hook都是同步执行的
SyncHook
串行同步执行,不关心返回值
class SyncHook { constructor(){ this.tasks = []; } tap(name,task){ this.tasks.push(task); } call(){ this.tasks.forEach(task=>task(...arguments)); }}let queue = new SyncHook(['name']);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.call('kbz');复制代码
SyncBailHook
串行同步执行,bail是保险丝的意思,有一个返回值不为null则跳过剩下的逻辑
class SyncBailHook { constructor(){ this.tasks = []; } tap(name,task){ this.tasks.push(task); } call(){ let i=0,ret; do { ret=this.tasks[i++](...arguments); } while (!ret); }}let queue = new SyncBailHook(['name']);queue.tap('1',function(name){ console.log(name,1); return 'Wrong';});queue.tap('2',function(name){ console.log(name,2);});queue.tap('3',function(name){ console.log(name,3);});queue.call('kbz');复制代码
SyncWaterfallHook
串行同步执行,Waterfall是瀑布的意思,前一个订阅者的返回值会传给后一个订阅者
class SyncWaterfallHook { constructor(){ this.tasks = []; } tap(name,task){ this.tasks.push(task); } call(){ let [first,...tasks]=this.tasks; tasks.reduce((ret,task)=>task(ret),first(...arguments)); }}let queue = new SyncWaterfallHook(['name']);queue.tap('1',function(name,age){ console.log(name,age,1); return 1;});queue.tap('2',function(data){ console.log(data,2); return 2;});queue.tap('3',function(data){ console.log(data,3);});queue.call('kbz',25);复制代码
SyncLoopHook
串行同步执行,Loop是循环往复的意思,订阅者返回true表示继续列表循环,返回undefine表示结束循环
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 == true || !(ret === undefined)) }); }}let queue = new SyncLoopHook(['name']);let count = 0;queue.tap('1',function(name){ console.log(count++); if(count==3){ return; }else{ return true; }});queue.call('kbz');复制代码
Async*型Hook
- ASync*型的Hook里面的Hook分为异步串行和异步并行两种
- ASync*型的Hook里面的Hook按照实现方式分为normal型、promise型
- ASync*型的Hook支持tapAsync、tapPromise注册,通过调用callAsync、promise方式调用。
对于promise还不清楚的可以参考
AsyncParallelHook
并行异步执行,和同步执行的最大区别在于,订阅者中可以存在异步逻辑。
- normal型
class AsyncParallelHook{ constructor() { this.tasks=[]; } tap(name,task) { this.tasks.push(task); } call() { let args=Array.from(arguments); let callback=args.pop(); //将全部任务执行完毕后执行的回调 let i=0,size = this.tasks.length; function done() { //用来统计订阅者异步任务执行完成的个数 if (++i == size) { callback(null); } } this.tasks.forEach(task => { task(...args,done); }); }}let queue = new AsyncParallelHook(['name']);console.time('cost');queue.tapAsync('1',function(name,callback){ setTimeout(function(){ console.log(1); callback(); },1000)});queue.tapAsync('2',function(name,callback){ setTimeout(function(){ console.log(2); callback(); },2000)});queue.tapAsync('3',function(name,callback){ setTimeout(function(){ console.log(3); callback(); },3000)});queue.callAsync('kbz',err=>{ console.log(err); console.timeEnd('cost');});复制代码
- promise型
class AsyncParallelHook{ constructor() { this.tasks=[]; } tapPromise(name,task) { this.tasks.push(task); } promise() { let promises = this.tasks.map(task => task()); //Promise.all所有的Promsie执行完成会调用回调 return Promise.all(promises); }}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('kbz').then(()=>{ console.timeEnd('cost');})复制代码
AsyncParallelBailHook
并行异步执行,bail是保险丝的意思,只要有一个异步逻辑返回不为null则会直接执行总的回调
- normal型
class AsyncParallelBailHook{ constructor() { this.tasks=[]; } tapAsync(name,task) { this.tasks.push(task); } callAsync() { let args=Array.from(arguments); let finalCallback=args.pop(); let count=0,total=this.tasks.length; function done(err) { if (err) { //如果有返回值,则直接执行总的回调 return finalCallback(err); } else { if (++count == total) { return finalCallback(); } } } for (let i=0;i{ console.log(err); console.timeEnd('cost');});复制代码
- promise型
class AsyncParallelBailHook{ constructor() { this.tasks=[]; } tapPromise(name,task) { this.tasks.push(task); } promise() { let args=Array.from(arguments); let promises = this.tasks.map(task => task(...arguments)); return Promise.all(promises); }}let queue = new AsyncParallelBailHook(['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); //错误直接调用reject,那么会自动调用promise会捕捉到 reject(); },2000) });});queue.tapPromise('3',function(name){ return new Promise(function(resolve,reject){ setTimeout(function(){ console.log(3); resolve(); },3000) });});queue.promise('kbz').then(()=>{ console.timeEnd('cost');},err => { console.error(err); console.timeEnd('cost');})复制代码
AsyncSeriesHook
串行异步执行,和并行异步执行的主要区别在于,会将下一个订阅的函数当成参数传给前一个订阅的函数,前一个订阅的函数控制运行。
- normal型
class AsyncSeriesBailHook{ constructor() { this.tasks=[]; } tapAsync(name,task) { this.tasks.push(task); } callAsync() { let args=Array.from(arguments); let callback=args.pop(); let i=0,size = this.tasks.length; let next=(err) => { let task=this.tasks[i++]; //将下一个订阅者传递给前一个订阅者调用 task?task(...args,next):callback(); } next(); }}let queue = new AsyncSeriesHook(['name']);console.time('cost');queue.tapAsync('1',function(name,callback){ setTimeout(function(){ console.log(1); },1000)});queue.tapAsync('2',function(name,callback){ setTimeout(function(){ console.log(2); callback(); },2000)});queue.tapAsync('3',function(name,callback){ setTimeout(function(){ console.log(3); callback(); },3000)});queue.callAsync('kbz',err=>{ console.log(err); console.timeEnd('cost');});复制代码
- promise型
class AsyncSeriesHook{ constructor() { this.tasks=[]; } tapPromise(name,task) { this.tasks.push(task); } promise() { let promises=this.tasks.map(item => item()); //将后一个promise放到前一个promise的then中执行,前一个执行完会自动执行then里面的异步逻辑 return promises.reduce((a,b) => a.then(()=>b)); }}let queue=new AsyncSeriesHook(['name']);console.time('cost');queue.tapPromise('1',function(name){ return new Promise(function(resolve){ setTimeout(function(){ console.log(1); resolve(); },1000) });});queue.tapPromise('2',function(name,callback){ return new Promise(function(resolve){ setTimeout(function(){ console.log(2); resolve(); },2000) });});queue.tapPromise('3',function(name,callback){ return new Promise(function(resolve){ setTimeout(function(){ console.log(3); resolve(); },3000) });});queue.promise('kbz').then(data=>{ console.log(data); console.timeEnd('cost');});复制代码
AsyncSeriesBailHook
串行异步执行,bail是保险丝的意思,只要有一个异步逻辑返回不为null则会跳出来直接执行最后的回调
class AsyncSeriesBailHook{ constructor() { this.tasks=[]; } tapAsync(name,task) { this.tasks.push(task); } callAsync() { let args=Array.from(arguments); let callback=args.pop(); let i=0,size = this.tasks.length; let next=(err) => { //如果返回的不是null则跳出后面逻辑,执行最后的回调 if (err) return callback(err); let task=this.tasks[i++]; task?task(...args,next):callback(); } next(); }}let queue = new AsyncSeriesBailHook(['name']);console.time('cost');queue.tapAsync('1',function(name,callback){ setTimeout(function(){ console.log(1); callback('wrong'); },1000)});queue.tapAsync('2',function(name,callback){ setTimeout(function(){ console.log(2); callback(); },2000)});queue.tapAsync('3',function(name,callback){ setTimeout(function(){ console.log(3); callback(); },3000)});queue.callAsync('kbz',err=>{ console.log(err); console.timeEnd('cost');});复制代码
- promise型
class AsyncSeriesHook{ constructor() { this.tasks=[]; } tapPromise(name,task) { this.tasks.push(task); } promise() { let promises=this.tasks.map(item => item()); //将后一个promise放到前一个promise的then中执行,前一个执行完会自动执行then里面的异步逻辑 return promises.reduce((a,b) => a.then(()=>b)); }}let queue=new AsyncSeriesHook(['name']);console.time('cost');queue.tapPromise('1',function(name){ return new Promise(function(resolve){ setTimeout(function(){ console.log(1); resolve(); },1000) });});queue.tapPromise('2',function(name,callback){ return new Promise(function(resolve){ setTimeout(function(){ console.log(2); reject(); //使用reject那么就会直接跳出后面的逻辑 },2000) });});queue.tapPromise('3',function(name,callback){ return new Promise(function(resolve){ setTimeout(function(){ console.log(3); resolve(); },3000) });});queue.promise('kbz').then(data=>{ console.log(data); console.timeEnd('cost');});复制代码
AsyncSeriesWaterfallHook
串行异步执行,Waterfall是瀑布的意思,前一个订阅者的返回值会传给后一个订阅者
- normal型
class AsyncSeriesWaterfallHook{ constructor() { this.tasks=[]; } tapAsync(name,task) { this.tasks.push(task); } callAsync() { let args=Array.from(arguments); let callback=args.pop(); let i=0,size = this.tasks.length; let next=(err,data) => { if (err) return callback(err); let task=this.tasks[i++]; if (task) { //除了第一个需要传arguments,后面的接受前一个的返回值 if (i==0) { task(...args,next); } else { task(data,next); } } else { callback(err,data); } } next(); }}let queue = new AsyncSeriesWaterfallHook(['name']);console.time('cost');queue.tapAsync('1',function(name,callback){ setTimeout(function(){ console.log(1); callback(null,1); },1000)});queue.tapAsync('2',function(data,callback){ setTimeout(function(){ console.log(2); callback(null,2); },2000)});queue.tapAsync('3',function(data,callback){ setTimeout(function(){ console.log(3); callback(null,3); },3000)});queue.callAsync('kbz',(err,data)=>{ console.log(err,data); console.timeEnd('cost');});复制代码
- promise型
class AsyncSeriesHook{ constructor() { this.tasks=[]; } tapPromise(name,task) { this.tasks.push(task); } promise() { let promises=this.tasks.map(item => item()); //将data传给下一个订阅者即可 return promises.reduce((a,b) => a.then((data)=>b)); }}let queue=new AsyncSeriesHook(['name']);console.time('cost');queue.tapPromise('1',function(name){ return new Promise(function(resolve){ setTimeout(function(){ console.log(1); resolve(1); },1000) });});queue.tapPromise('2',function(name,callback){ return new Promise(function(resolve){ setTimeout(function(){ console.log(2); resoleve(2); },2000) });});queue.tapPromise('3',function(name,callback){ return new Promise(function(resolve){ setTimeout(function(){ console.log(3); resolve(3); },3000) });});queue.promise('kbz').then(data=>{ console.log(data); console.timeEnd('cost');});复制代码
结语:
tapable就是基于发布订阅机制实现的,这样能够用队列存储一系列的回调,在webapck的生命周期的各个阶段调用。