脑袋疼的Webpack-tapable

tapable据说了好久,终于下定决心系统学习一下javascript

Q1:tapable解决的问题?

  1. tapable是个独立的库
  2. webpack中大量使用了这个库
  3. tapable主要是用来处理事件,解决的问题有点相似EventEmitter,不过功能更增强大

Q2:tapable方法有哪些?

const {
    SyncHook,
    SyncBailHook,
    SyncWaterfallHook,
    SyncLoopHook,
    AsyncParallelHook,
    AsyncParallelBailHook,
    AsyncSeriesHook,
    AsyncSeriesBailHook,
    AsyncSeriesWaterfallHook 
 } = require("tapable");
复制代码

好的,方法一共是上述这么多,第一眼看过去,懵逼树下你和我,因此咱们仍是一点点来,一个个的分析、学习和了解前端

Q3:啥是SyncHook?

先来个使用的例子,例如前端开发者须要掌握哪些技能?java

step1:首先咱们要明确群体是前端开发

const {SyncHook}= require('tapable');
const FrontEnd = new SyncHook();
复制代码

ok,就是上面这两句,咱们建立了个FrontEnd前端开发node

step2:前端开发须要掌握哪些技能,例如webpack、react对吧

FrontEnd.tap('webpack',()=>{
  console.log("get webpack")
});
FrontEnd.tap('react',()=>{
  console.log("get react")
});
复制代码

ok,上面的tap就是用来绑定事件的,为前端开发添加了两个技能react

step3:技能须要学习才能掌握,因此咱们要有学习的动做

FrontEnd.learn=()=>{
  FrontEnd.call()
};
FrontEnd.learn();
复制代码

step4:查看执行结果

get webpack
get react
复制代码

能够看到,经过上面的调用,咱们的前端开发已经学会了react、webpackjquery

step5:传参

前面知道FrontEnd这个群体,须要学react、webpack,但落到我的角度,究竟哪个开发者掌握这些技能了呢?webpack

const {SyncHook}= require('tapable');
const FrontEnd = new SyncHook();
FrontEnd.tap('webpack',(name)=>{
  console.log(name+" get webpack")
});
FrontEnd.tap('react',(name)=>{
  console.log(name+" get react")
});
FrontEnd.start=(name)=>{
  FrontEnd.call(name)
};
FrontEnd.start('xiaoming');
复制代码

修改前面的代码,添加参数,预期是输出xxx get reactweb

step6: 查看输出结果

undefined get webpack
undefined get react
复制代码

最终结果是undefined,也就是参数没传进去redux

step7:为SyncHook添加约定参数

这是由于const FrontEnd = new SyncHook();建立SyncHook的时候没有约定参数,只要为其添加参数便可,以下:api

const {SyncHook}= require('tapable');
const FrontEnd = new SyncHook(['name']);// 添加参数约定
FrontEnd.tap('webpack',(name)=>{
  console.log(name+" get webpack")
});
FrontEnd.tap('react',(name)=>{
  console.log(name+" get react")
});
FrontEnd.start=(name)=>{
  FrontEnd.call(name)
};
FrontEnd.start('xiaoming');
复制代码

最终输出:

xiaoming get webpack
xiaoming get react
复制代码

SyncHook总结

  1. SyncHook目前来看比较像订阅发布
  2. 就像jquery中的add、fire方法,只不过这里是tap、call

Q4:SyncHook如何实现?

SyncHook实现比较简单,就是最简单的订阅发布

class SyncHook {
  constructor(limit = []){
    this.limit= limit;
    this.tasks = [];
  }
  tap(name,task){
    this.tasks.push(task);
  }
  call(...args){
    const param =  args.slice(0,this.limit.length);
    this.tasks.forEach(item=>item(...param));
  }
}
复制代码
  1. limit是用来作参数校验的
  2. tasks用来收集订阅
  3. tap方法用来想tasks中添加方法
  4. call方法,先检验参数,而后再执行全部的已订阅方法

总结:原理比较简单,没有太多技术含量,主要就是一个同步的钩子函数

Q5:啥是SyncBailHook?

熔断机制,若是前一个事件return true,则再也不执行下一个,仍是前面的例子:

const {SyncBailHook} =require('tapable');
const FrontEnd = new SyncBailHook(['name']);
FrontEnd.tap('webpack',(name)=>{
  console.log(name+" get webpack ")
});
FrontEnd.tap('react',(name)=>{
  console.log(name+" get react")
});
FrontEnd.start=(...args)=>{
  FrontEnd.call(...args)
};
FrontEnd.start('xiaoming');
复制代码

此时,把函数从SyncHook换成SyncBailHook,执行的结果没有任何区别

but,思考一下,学习很容易会学不下去,因此修改一下咱们的例子:

const {SyncBailHook} =require('tapable');
const FrontEnd = new SyncBailHook(['name']);
FrontEnd.tap('webpack',(name)=>{
  console.log(name+" get webpack ")
  return '学不动了啊!';
});
FrontEnd.tap('react',(name)=>{
  console.log(name+" get react")
});
FrontEnd.start=(...args)=>{
  FrontEnd.call(...args)
};
FrontEnd.start('xiaoming');
复制代码

此时仅输出:

xiaoming get webpack 
复制代码

后面的react没有执行

总结:

  1. SyncBailHook主要解决的问题是条件阻塞
  2. 当订阅事件符合某一判断时,再也不执行下面的流程
  3. 应用场景,场景不断深刻的场景,a、a+b、a+b+c、a+b+c+d这种场景

Q6:SyncBailHook如何实现?

SyncBailHook也十分简单,仍是以前那个例子:

class SyncBailHook {
  constructor(limit = []){
    this.limit= limit;
    this.tasks = [];
  }
  tap(name,task){
    this.tasks.push(task);
  }
  call(...args){
    const param =  args.slice(0,this.limit.length);
    this.tasks.some(item=>item(...param));// 只改了一行
  }
}
复制代码

能够看到,和上面SyncHook十分类似,无非就是把执行函数forEach,换成some,由于some是阻塞式执行,当返回true,则不会执行后面的内容

Q7:啥是SyncWaterfullHook?

仍是先来个使用的例子,例如前端,技能都是一个个学的,要学完webpack再学react,例如:

const {SyncWaterfallHook} = require('tapable');
const FrontEnd = new SyncWaterfallHook(['name']);
FrontEnd.tap('webpack',(name)=>{
  console.log(name+" get webpack ")
  return '学完webpack了,该学react了';
});
FrontEnd.tap('react',(name)=>{
  console.log(name+" get react")
});
FrontEnd.start=(...args)=>{
  FrontEnd.call(...args)
};
FrontEnd.start('xiaoming');
复制代码

此时输出:

xiaoming get webpack 
学完webpack了,该学react了 get react
复制代码
  1. SyncWaterfallHook会将前一个任务的执行结果,传递给后一个
  2. 主要使用场景是处理逻辑之间相互依赖
  3. 实际效果和redux中的compose方法一毛同样

Q8:SyncWaterfullHook如何实现?

class SyncWaterfallHook {
  constructor(limit = []){
    this.limit= limit;
    this.tasks = [];
  }
  tap(name,task){
    this.tasks.push(task);
  }
  call(...args){
    const param =  args.slice(0,this.limit.length);
    const [first,...others] = this.tasks;
    const ret = first(...param);
    others.reduce((pre,next)=>{
      return next(pre);
    },ret)
  }
}
复制代码

SyncWaterfallHook实现也比较简单

  1. 彻底按照redux的compose来实现就行
  2. 第一步,取出第一个执行,并拿到结果ret
  3. 第二步,将结果ret,看成reduce的参数传递进去
  4. 第三步,遍历,不断把参数传给下一个函数

总结:SyncWaterfallHook主要仍是用于函数之间对结果存在依赖的场景

Q9:啥是SyncLoopHook?

仍是前面的例子,若是一次学不懂一门技术,那就要多学几遍,例如:

const FrontEnd = new SyncLoopHook(['name']);
let num = 0;
FrontEnd.tap('webpack',(name)=>{
  console.log(name+" get webpack ")
  return ++num === 3?undefined:'再学一次';
});
FrontEnd.tap('react',(name)=>{
  console.log(name+" get react")
});
FrontEnd.start=(...args)=>{
  FrontEnd.call(...args)
};
FrontEnd.start('xiaoming');
复制代码

上面执行的结果是:

xiaoming get webpack 
xiaoming get webpack 
xiaoming get webpack 
xiaoming get react
复制代码
  1. SyncLoopHook任务可以执行屡次
  2. 返回undefined则中止执行,返回非undefined则继续执行当前任务

总结:主要场景是同一任务,须要执行屡次

Q10:SyncLoopHook如何实现?

class SyncLoopHook {
  constructor(limit = []){
    this.limit= limit;
    this.tasks = [];
  }
  tap(name,task){
    this.tasks.push(task);
  }
  call(...args){
    const param =  args.slice(0,this.limit.length);
    let index = 0;
    while(index<this.tasks.length){
      const result = this.tasks[index](...param);
      if(result === undefined){
        index++;
      }
    }
  }
}
复制代码
  1. 上面的实现是经过计数
  2. 若是结果不为undefined则下标index不移动
  3. 若是结果为undefined则下标index增长

也能够换doWhile来实现

class SyncLoopHook {
  constructor(limit = []){
    this.limit= limit;
    this.tasks = [];
  }
  tap(name,task){
    this.tasks.push(task);
  }
  call(...args){
    const param =  args.slice(0,this.limit.length);
   this.tasks.forEach(task=>{
     let ret;
     do{
      ret = task(...param);
     }while(ret!=undefined)
   })
  }
}
复制代码
  1. 这种实现没有下标概念了
  2. 直接遍历tasks任务组,若是任务组中某一个任务执行的结果不是undefined则再次执行

总结:SyncLoopHook这个使用场景相对较少,不过了解一下也好

Q11:啥是AsyncParralleHook?

前面了解的都是同步hook,更关键的是异步hook

举个例子,同窗小王说去学前端了,但你也不知道他何时学完,只有他学完告诉你,你才知道他学完了,例:

const {AsyncParallelHook} = require('tapable');
const FrontEnd = new AsyncParallelHook(['name']);
FrontEnd.tapAsync('webpack',(name,cb)=>{
  setTimeout(() => {
    console.log(name+" get webpack ")
    cb();
  }, 1000);
 
});
FrontEnd.tapAsync('react',(name,cb)=>{
  setTimeout(() => {
    console.log(name+" get react")
    cb();
  }, 1000);
});
FrontEnd.start=(...args)=>{
  FrontEnd.callAsync(...args,()=>{
    console.log("end");
  })
};
FrontEnd.start('小王');
复制代码

最终输出:

小王 get webpack 
小王 get react
end
复制代码
  1. AsyncParralleHook是异步并行钩子
  2. 使用场景,例如同时发起对两个接口的请求
  3. 注意:此次注册事件,再也不是tap了,而是tapAsync
  4. 注意:此次的事件执行,再也不是call了,而是callAsync
  5. 能够看出tapable中区分了同步、异步的订阅和发布
  6. 注意:想要让全部异步执行完成后,接收到通知,须要执行cb()

Q12:AsyncParralleHook如何实现?

class AsyncParallelHook {
  constructor(limit = []){
    this.limit= limit;
    this.tasks = [];
  }
  tapAsync(name,task){
    this.tasks.push(task);
  }
  callAsync(...args){
    const finalCallBack = args.pop();
    const param =  args.slice(0,this.limit.length);
    let index = 0;
    const done=()=>{
      index++;
      if(index === this.tasks.length){
        finalCallBack();
      }
    }
    this.tasks.forEach(item=>item(...param,done))
  }
}
复制代码
  1. AsyncParallelHook最简单就是经过计数
  2. 在实例上添加一个计数器
  3. 而后遍历tasks,当任务成功个数与任务总数相同时,执行finalCallBack

总结:AsyncParallelHook解决的问题和promise.all相似,都是用于解决异步并行的问题

Q13:AsyncParralleHook(2)如何使用promise?

前面虽然用:AsyncParralleHook可以解决异步,但并无使用primise,也没有类promise的概念

const {AsyncParallelHook} = require('tapable');
const FrontEnd = new AsyncParallelHook(['name']);
FrontEnd.tapPromise('webpack',(name)=>{
  return new Promise((resolve)=>{
    setTimeout(() => {
      console.log(name+" get webpack ")
      resolve();
    }, 1000);
  })
});
FrontEnd.tapPromise('react',(name,cb)=>{
  return new Promise((resolve)=>{
    setTimeout(() => {
      console.log(name+" get react ")
      resolve();
    }, 1000);
  })
});
FrontEnd.start=(...args)=>{
  FrontEnd.promise(...args).then(()=>{
    console.log("end");
  })
};
FrontEnd.start('小王');
复制代码

调用上面的api后,输出:

小王 get webpack 
小王 get react 
end
复制代码
  1. 注意:此时绑定事件的方法叫作tapPromise
  2. 注意:此时执行事件的方法叫作promise

总结:

  1. tapable共有三种事件绑定方法:tap、tapAsync、tapPromise
  2. tapable共有三种事件执行方法:call、callAsync、promise

Q14:AsyncParralleHook(2)promise版如何实现?

class AsyncParallelHook {
  constructor(limit = []){
    this.limit= limit;
    this.tasks = [];
  }
  tapPromise(name,task){
    this.tasks.push(task);
  }
  promise(...args){
    const param =  args.slice(0,this.limit.length);
    const tasks = this.tasks.map(task=>task(...param));
    return Promise.all(tasks)
  }
}
复制代码
  1. 核心就是实现两个方法,tapPromise和promise
  2. tapPromise其实和以前的tap没有明显区别(简单实现的问题)
  3. promise的话,其实就是返回一个Promise.all

Q15:啥是AsyncParallelBailHook?

AsyncParallelBailHook这个钩子和前面的钩子不太同样 按前面的例子来说:

  1. 同窗小王说去学前端了,但你也不知道他何时学完,只有他学完告诉你,你才知道他学完了
  2. 小王学了webpack,学崩了,告诉了你
  3. 你据说小王学崩了,你就觉得他学不下去了,你就对你们伙说,小王学崩了
  4. 可是小王同时也学了react却咬牙学完了
  5. 虽然学完了,但你已经对外宣布小王崩了,很打脸,因此就当不知道了

这就是AsyncParallelBailHook处理的事情

const {AsyncParallelBailHook} = require('tapable');
const FrontEnd = new AsyncParallelBailHook(['name']);
FrontEnd.tapPromise('webpack',(name)=>{
  return new Promise((resolve,reject)=>{
    setTimeout(() => {
      console.log(name+" get webpack ")
      reject('小王学崩了!');
    }, 1000);
  })
});
FrontEnd.tapPromise('react',(name,cb)=>{
  return new Promise((resolve)=>{
    setTimeout(() => {
      console.log(name+" get react ")
      resolve();
    }, 2000);
  })
});
FrontEnd.start=(...args)=>{
  FrontEnd.promise(...args).then(()=>{
    console.log("end");
  },(err)=>{
    console.log("据说:",err)
  })
};
FrontEnd.start('小王');
复制代码

上面代码执行结果是:

小王 get webpack 
据说: 小王学崩了!
小王 get react 
复制代码
  1. 上面例子,第一个并行任务返回了reject
  2. reject只要不是undefined,就会直接进入promise.all的catch
  3. 异步任务,react仍是会执行,但成功后没有处理了

再看一个例子:

const {AsyncParallelBailHook} = require('tapable');
const FrontEnd = new AsyncParallelBailHook(['name']);
FrontEnd.tapPromise('webpack',(name)=>{
  return new Promise((resolve,reject)=>{
    setTimeout(() => {
      console.log(name+" get webpack ")
      reject();
    }, 1000);
  })
});
FrontEnd.tapPromise('react',(name,cb)=>{
  return new Promise((resolve)=>{
    setTimeout(() => {
      console.log(name+" get react ")
      resolve();
    }, 2000);
  })
});
FrontEnd.start=(...args)=>{
  FrontEnd.promise(...args).then(()=>{
    console.log("end");
  },(err)=>{
    console.log("据说:",err)
  })
};
FrontEnd.start('小王');
复制代码

和上面就改了1行,就是reject内容为空,此时输出:

小王 get webpack 
小王 get react 
end
复制代码
  1. 此时即使调用了reject也不会进入到catch
  2. reject返回空,后面的任务也会照常执行

总结:

  1. AsyncParallelBailHook,若是返回真值,则直接会走进catch
  2. 不管返回结果是什么,全部任务都会执行
  3. 主要场景是,并行请求3个接口,随便哪个返回结果都行,只要返回了,就对返回进行处理(走catch)
  4. 若是用来处理同步,则和SyncBailHook效果同样
  5. 若是处理tapSync,则遇到return true最终的callback不会执行
  6. 若是处理promise,则遇到rejcet(true),则直接进入catch

Q16:AsyncParallelBailHook如何实现?

这个AsyncParallelBailHook真真烧脑了好一会

class AsyncParallelBailHook {
  constructor(limit = []){
    this.limit= limit;
    this.tasks = [];
  }
  tapPromise(name,task){
    this.tasks.push(task);
  }
  promise(...args){
    const param =  args.slice(0,this.limit.length);
    const tasks = this.tasks.map(task=>{
      return new Promise((resolve,reject)=>{
        task(...param).then((data)=>{
          resolve(data);
        },(err)=>{
          err? reject(err):resolve();
        });
      })
    });
    return Promise.all(tasks)
  }
}
复制代码
  1. 正常状况下,promise.all中任意一个任务reject,就会进入统一的catch
  2. 但咱们须要的是根据reject的值来判断是否走如catch
  3. 因此咱们在原有task外,再包一层promise
  4. 若是reject值为真,则执行reject
  5. 若是reject值为假,则执行resolve,就当什么也没发生

Q17:啥是AsyncSeriesHook?

前面讲的是异步并行,如今该说异步串行了,例如小王,学完webpack才去学的react,你也不知道他何时学完,但他学完一个就会告诉你一下,例:

const {AsyncSeriesHook} = require('tapable');
const FrontEnd = new AsyncSeriesHook(['name']);
console.time('webpack');
console.time('react');
FrontEnd.tapPromise('webpack',(name,cb)=>{
  return new Promise((resolve,reject)=>{
    setTimeout(() => {
      console.log(name+" get webpack ")
      console.timeEnd('webpack');
      resolve();
    }, 1000);
  })
});
FrontEnd.tapPromise('react',(name,cb)=>{
  return new Promise((resolve)=>{
    setTimeout(() => {
      console.log(name+" get react ")
      console.timeEnd('react');
      resolve();
    }, 1000);
  })
});
FrontEnd.start=(...args)=>{
  FrontEnd.promise(...args).then(()=>{
    console.log("end");
  })
};
FrontEnd.start('小王');
复制代码

上面代码执行结果:

小王 get webpack 
webpack: 1010.781ms
小王 get react 
react: 2016.598ms
end
复制代码
  1. 两个异步任务,变成了串行
  2. 从时间可以得出,两个1s的异步的任务,串行后总时间变成了2s

总结:AsyncSeriesHook解决的问题是异步串行,例如node的os.cpus()有限,能够把任务分批次执行,这样对性能有保障

Q18:AsyncSeriesHook如何实现?

class AsyncSeriesHook {
  constructor(limit = []){
    this.limit= limit;
    this.tasks = [];
  }
  tapPromise(name,task){
    this.tasks.push(task);
  }
  promise(...args){
    const param =  args.slice(0,this.limit.length);
    const [first,...others] = this.tasks;
    return others.reduce((pre,next)=>{
      return pre.then(()=>next(...param))
    },first(...param))
  }
}
复制代码
  1. 实现核心就是promise串行
  2. 取出第一个任务,执行拿到promise实例,而后经过reduce遍历

Q19:啥是AsyncSeriesBailHook?

仍是前面的例子,若是小王学前端,学了webapck就完全放弃了,那后面的react也就不用学了

const {AsyncSeriesBailHook} = require('tapable');
const FrontEnd = new AsyncSeriesBailHook(['name']);
console.time('webpack');
console.time('react');
FrontEnd.tapPromise('webpack',(name,cb)=>{
  return new Promise((resolve,reject)=>{
    setTimeout(() => {
      console.log(name+" get webpack ")
      console.timeEnd('webpack');
      reject('小王完全放弃了');
    }, 1000);
  })
});
FrontEnd.tapPromise('react',(name,cb)=>{
  return new Promise((resolve)=>{
    setTimeout(() => {
      console.log(name+" get react ")
      console.timeEnd('react');
      resolve();
    }, 1000);
  })
});
FrontEnd.start=(...args)=>{
  FrontEnd.promise(...args).then(()=>{
    console.log("end");
  }).catch((err)=>{
    console.log("err",err)
  })
};
FrontEnd.start('小王');
复制代码

上面代码输出:

小王 get webpack 
webpack: 1010.518ms
err 小王完全放弃了
复制代码
  1. 上面的代码只执行到webpack
  2. AsyncSeriesBailHook,任务若是return,或者reject,则阻塞了

场景:主要是异步串行,若是某一个任务执行的结果reject或者return,那么后面的都将再也不执行

Q20:AsyncSeriesBailHook如何实现?

class AsyncSeriesBailHook {
  constructor(limit = []){
    this.limit= limit;
    this.tasks = [];
  }
  tapPromise(name,task){
    this.tasks.push(task);
  }
  promise(...args){
    const param =  args.slice(0,this.limit.length);
    const [first,...others] = this.tasks;
    return new Promise((resolve,reject)=>{
      others.reduce((pre,next,index,arr)=>{
        return pre
          .then(()=>next(...param))
          .catch((err=>{
            arr.splice(index,arr.length-index);
            reject(err);
          })).then(()=>{
            (index+1 === arr.length) && resolve();
          })
      },first(...param))
    })
  }
}
复制代码

AsyncSeriesBailHook实现难度要高不少

  1. 首先在reduce外再包一层promise
  2. 当遇到任何一个子任务进入catch的时候,则将reduce的第四个参数arr切割,使其没法再向下进行,也就是中止reduce的继续
  3. 同时全部promise后面再添加一个后置then,用来检测是否所有执行完成
  4. 为何使用index+1,是由于后置then确定是最后一个任务,但遍历index还处于上一个下标,因此只要加1就好

Q21:啥是AsyncSeriesWaterfallHook?

SyncWaterFallHook前面已经了解过了,就是前一个执行完的结果会传递给下一个执行函数,和AsyncSeriesWaterfallHook的区别就是,一个是同步一个是异步

具体来讲,例如只有一本教材,小王学完,小张才能学

const FrontEnd = new AsyncSeriesWaterfallHook(['name']);
FrontEnd.tapAsync('webpack',(name,cb)=>{
    setTimeout(() => {
      console.log(name+" get webpack ")
      cb(null,'小李');
    }, 1000);
});
FrontEnd.tapAsync('webpack',(name,cb)=>{
  setTimeout(() => {
    console.log(name+" get webpack ")
    cb(null,'小张');
  }, 1000);
});
FrontEnd.tapAsync('webpack',(name,cb)=>{
  setTimeout(() => {
    console.log(name+" get webpack ")
    cb(null,'小红');
  }, 1000);
});
FrontEnd.start=(...args)=>{
  FrontEnd.callAsync(...args,(data)=>{
    console.log("全学完了",)
  })
};
FrontEnd.start('小王');
复制代码

上面代码,最终输出:

小王 get webpack 
小李 get webpack 
小张 get webpack 
全学完了
复制代码

总结:这个的用法和SyncWaterFallHook的用法一致

Q22:AsyncSeriesWaterfallHook如何实现?

class AsyncSeriesWaterfallHook {
  constructor(limit = []){
    this.limit= limit;
    this.tasks = [];
  }
  tapAsync(name,task){
    this.tasks.push(task);
  }
  callAsync(...args){
    const param =  args.slice(0,this.limit.length);
    const finalCallBack = args.pop();
    let index = 0;
    const next = (err,data)=>{
      const task = this.tasks[index];
      if(!task)return finalCallBack();
      if(index === 0){
        task(...param,next)
      }else{
        task(data,next)
      }
      index++;
    }
    next();
  }
}
复制代码
  1. 主要是经过封装一个回调函数next
  2. 而后不断调用任务队列中的任务,调用的时候,再传递相同的回调函数进去

prmise版本的实现以下:

class AsyncSeriesWaterfallHook {
  constructor(limit = []){
    this.limit= limit;
    this.tasks = [];
  }
  tapPromise(name,task){
    this.tasks.push(task);
  }
  promise(...args){
    const param =  args.slice(0,this.limit.length);
    const [first,...others] = this.tasks;
    return others.reduce((pre,next)=>{
      return pre.then((data)=>{
        return data?next(data):next(...param);
      })
    },first(...param))
  }
}
复制代码
  1. promise的实现要相对简单一些
  2. 主要去看then方法中是否有内容,若是有的话,则传递个下一个函数,若是没有,则用初始参数

总结

  1. tapable的各AsyncHook都同时支持tap、tapAsync、tapPromise
  2. tapable主要解决的是事件流转的问题,各个Hook使用的场景各有不一样
  3. tapable主要应用在webpack的各个生命周期中,具体的实践还须要结合webpack原理去看

引用

tapable使用指南

相关文章
相关标签/搜索