Tapable是基於發佈訂閱模式實現的一個類庫,提供了許多Hook類,可建立許多鉤子。在這些鉤子裏面調用內置或者用戶在webpack.config.js中使用的插件,webpack在編譯打包代碼的各個環節會使用到。 webpack
Sync*型的Hook裏面的Hook都是同步執行的web
串行同步執行,不關心返回值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');
複製代碼
串行同步執行,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');
複製代碼
串行同步執行,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);
複製代碼
串行同步執行,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');
複製代碼
對於promise還不清楚的能夠參考promise原理就是這麼簡單oop
並行異步執行,和同步執行的最大區別在於,訂閱者中能夠存在異步邏輯。post
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');
});
複製代碼
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');
})
複製代碼
並行異步執行,bail是保險絲的意思,只要有一個異步邏輯返回不爲null則會直接執行總的回調優化
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');
});
複製代碼
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');
})
複製代碼
串行異步執行,和並行異步執行的主要區別在於,會將下一個訂閱的函數當成參數傳給前一個訂閱的函數,前一個訂閱的函數控制運行。ui
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');
});
複製代碼
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');
});
複製代碼
串行異步執行,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');
});
複製代碼
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');
});
複製代碼
串行異步執行,Waterfall是瀑布的意思,前一個訂閱者的返回值會傳給後一個訂閱者
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');
});
複製代碼
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的生命週期的各個階段調用。