webapck由浅入深——(Tapable)

##webpack系列文章

  1. webpack由浅入深——(webpack基础配置)
  2. webpack由浅入深——(webpack优化配置)
  3. webpack由浅入深——(tapable)
  4. webpack由浅入深——(ast)
  5. webpack由浅入深——(webapck简易版)
  6. webpack由浅入深——(loader实现)
  7. webpack由浅入深——(plugin实现)

Tapable和webpack

Tapable是基于发布订阅模式实现的一个类库,提供了许多Hook类,可创建许多钩子。在这些钩子里面调用内置或者用户在webpack.config.js中使用的插件,webpack在编译打包代码的各个环节会使用到。

webpack流程图

 

Tapable中Hook分类

  • Taptable:
    • Sync*:
      • SyncHook
      • SyncBailHook
      • SyncWaterfallHook
      • SyncLoopHook
    • Async*:
      • AsyncParallelHook
      • AsyncParallelBailHook
      • AsyncSeriesHook
      • AsyncSeriesBailHook
      • AsyncSeriesWaterfallHook

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;i<total;i++){
            let 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的生命周期的各个阶段调用。


作者:梦想攻城狮
链接:https://juejin.im/post/5bd4290c51882526f11d55c6
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

猜你喜欢

转载自blog.csdn.net/sinat_17775997/article/details/83477237