webpack系列文章
- webpack由浅入深——(webpack基础配置)
- webpack由浅入深——(webpack优化配置)
- webpack由浅入深——(tapable)
- webpack由浅入深——(webapck简易版)
- webpack由浅入深——(ast、loader和plugin)
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还不清楚的可以参考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;ilet task=this.tasks[i];
task(...args,done);
}
}
}
let queue=new AsyncParallelBailHook(['name']);
console.time('cost');
queue.tapAsync('1',function(name,callback){
console.log(1);
callback('Wrong');
});
queue.tapAsync('2',function(name,callback){
console.log(2);
callback();
});
queue.tapAsync('3',function(name,callback){
console.log(3);
callback();
});
queue.callAsync('kbz',err=>{
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的生命周期的各个阶段调用。