webapck由淺入深——(Tapable)

webpack系列文章

  1. webpack由淺入深——(webpack基礎配置)
  2. webpack由淺入深——(webpack優化配置)
  3. webpack由淺入深——(tapable)
  4. webpack由淺入深——(webapck簡易版)
  5. webpack由淺入深——(ast、loader和plugin)

Tapable和webpack

Tapable是基於發佈訂閱模式實現的一個類庫,提供了許多Hook類,可建立許多鉤子。在這些鉤子裏面調用內置或者用戶在webpack.config.js中使用的插件,webpack在編譯打包代碼的各個環節會使用到。 webpack

webpack流程圖

Tapable中Hook分類

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

Sync*型Hook

Sync*型的Hook裏面的Hook都是同步執行的web

SyncHook

串行同步執行,不關心返回值promise

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則跳過剩下的邏輯bash

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原理就是這麼簡單oop

AsyncParallelHook

並行異步執行,和同步執行的最大區別在於,訂閱者中能夠存在異步邏輯。post

  • 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

串行異步執行,和並行異步執行的主要區別在於,會將下一個訂閱的函數當成參數傳給前一個訂閱的函數,前一個訂閱的函數控制運行。ui

  • 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的生命週期的各個階段調用。

相關文章
相關標籤/搜索