大家在看webpack源码的时候,有没有感觉像再看天书,似乎没有办法一个文件比如webpack.js从头看到尾。感觉webpack的跳跃性很强,完全不知道程序在运行的时候,发生了什么。完全不清楚这个事件是什么时候发生的,比如loader是什么时候执行的,plugin又是什么时候出现的。webpack的程序错综复杂,完全迷失在程序之中。这究竟是为什么呢?其实很简单!因为webpack的灵魂Tapable!这个机制使得webpack异常的灵活,它有一句经典的话——Everything is a plugin!。由此可见webpack是靠插件堆积起来的。而实现这个插件机制的就是Tabable!
Eventswebpack的灵魂Tapable,有点类似于nodejs的Events,都是注册一个事件,然后到了适当的时候触发。这里的事件触发是这样绑定触发的,通过on方法,绑定一个事件,emit方法出发一个事件。Tapable的机制和这类似,也是tap注册一个事件,然后call执行这个事件。
const EventEmitter = require('events'); const myEmitter = new EventEmitter(); //on的第一个参数是事件名,之后emit可以通过这个事件名,从而触发这个方法。 //on的第二个参数是回掉函数,也就是此事件的执行方法 myEmitter.on('newListener', (param1,param2) => { console.log("newListener",param1,param2) }); //emit的第一个参数是触发的事件名 //emit的第二个以后的参数是回调函数的参数。 myEmitter.emit('newListener',111,222); Tapable究竟为何物如果我们把Tapable的实例对象比作一颗参天大树,那么的每一根树枝就是一个挂载的hook(钩子),也就是Tapable之中给每一个事件分门别类的机制,比如编译(compile.js)这个对象中,又运行(run)的钩子,有构建(make)的钩子,这些钩子就像树枝一样,组成了一棵树的骨干,然后每个树枝上的树叶就是每个钩子上面挂载的函数方法。树枝(钩子)越多,树叶(函数)越多,此树越茂密(程序越复杂)。
当然这只是一个简易的理解。实际上,webpack中不止有一棵树,每棵树之间还有错综复杂的关系。比如有些方法如compilation.js中的一些方法,就要等compile.js中的make这个钩子执行之后才会执行。那么我们就从了解Tapable中钩子的用法,来理解webpack中tapable。
以工作日为例,了解Tapable的用法即使webpack中的每颗tapable的树之间有错综复杂的关系,整个程序都有一个逻辑线,也就是游戏中的主线剧情,我们先构建我们工作日的主线剧情。
主线剧情让我们来回一下,我们的日常工作日,应该大多数分成3个阶段,上班前,上班中和下班后,这3个时间段。这三个时间段,我用了3中钩子类型,普通型,流水型和熔断型。
按照文档他们的解释是这样的:
普通型basic:这个比较好理解就是按照tap的注册顺序一个个向下执行。
流水型water:这个相对于basic的区别就是,虽然也是按照tap的顺序一个个向下执行,但是如果上一个tap有返回值,那么下一个tap的传入参数就是上一个tap的返回值。
熔断型bail:这个相对于water的区别就是,如果返回了null以外的值,就不继续执行了。
是不是感觉一个事件的订阅发布怎么可以分出这么多类型?不要急,每个类型都有他的作用!
钩子的语法一般都是new 钩子类型Hook([参数名1,参数名2,参数名3]),这里的数组是只是提示你传入参数有几个,给了名字只是为了可读性,如果你想写一个别人看不懂的可以这样new SyncHook(["a","b","c"]),这里要注意这个参数名的类型是字符串。如果没有提前准备号需要传入的参数,后续挂函数的时候,就无法传入参数了。这个设计应该是为了日后好打理,告诉其他开发者,我传入的参数类型。
class MyDaily { constructor() { this.hooks = { beforeWork: new SyncHook(["getUp"]), atWork: new SyncWaterfallHook(["workTask"]), afterWork: new SyncBailHook(["activity"]) }; } tapTap(){ //此处是行为 } run(){ this.hooks.beforeWork.call() this.hooks.atWork.call() this.hooks.afterWork.call() } }一天我们不可能什么事都不做,所以给钩子上加点事,tap事情。先来点必然发生的,正常的上班族,自由职业不在考虑范围内。早上我们会做什么呢?穿衣服出门是必备的,不穿衣服没法出门,不出门没法上班。到了工作岗位,来点工作任务吧,比如我们需要做个ppt,然后用这个ppt去开会。下班后,本来想回家的,结果佳人有约,果然不回家。
tapTap(){ this.hooks.beforeWork.tap("putOnCloth",()=>{ console.log("穿衣服!") }) this.hooks.beforeWork.tap("getOut",()=>{ console.log("出门!") }) this.hooks.atWork.tap("makePPT",()=>{ console.log("做PPT!") return "你的ppt" }) this.hooks.atWork.tap("meeting",(work)=>{ console.log("带着你的"+work+"开会!") }) this.hooks.afterWork.tap("haveADate",()=>{ console.log("约会咯!") return "约会真开心~" }) this.hooks.afterWork.tap("goHome",()=>{ console.log("溜了溜了!") }) }