Webpack的整个流程是通过tapable的事件流机制串联起来。理解tapable对于阅读源代码的作用极大。本文分析的Tapable源码的版本是0.2.7。
一、tapable用途
tapable中大量的API主要是为了实现两个功能,一个作用是通过apply方法在complier中注册插件;另一个作用就是实现事件机制。
二、tapable中的apply方法
apply方法的源码:
源码意思很明显,就是调用传入对象的apply方法。在webpack中,这个传入的对象就是插件的实例对象。
一个插件的简单实现大致如下所示:
实际的例子如下:
var compiler = new Complier();
compiler.apply(new SimplePlugin());
三、实现事件机制的其他API
tapable中其他的API都是为实现事件机制准备。webpack中的事件机制正是通过tapable实现的。需要进行事件处理的类通过继承tapable类拥有事件处理的能力。下面我们先看看tapable中是如何实现事件机制的。
所谓的事件处理,就是触发事件和监听事件。
触发事件的方法:applyPlugins、applyPlugins0、applyPlugins1、applyPlugins2;applyPluginsWaterfall、applyPluginsWaterfall0、applyPluginsWaterfall1、applyPluginsWaterfall2;applyPluginsBailResult、applyPluginsBailResult1、applyPluginsBailResult2、applyPluginsBailResult3、applyPluginsBailResult4、applyPluginsBailResult5;applyPluginsAsyncSeries、applyPluginsAsync、applyPluginsAsyncSeries1;applyPluginsAsyncSeriesBailResult、applyPluginsAsyncSeriesBailResult1;applyPluginsAsyncWaterfall;applyPluginsParallel;applyPluginsParallelBailResult、applyPluginsParallelBailResult1。
监听事件的方法:plugin
webpack中实现事件机制大概分为三步:
第一步、需要实例化Tapable类或者实例化继承自tapable的类
var tapable = new Tapable();
第二步、监听特定的事件(相当于JS中的addEventListerner)
tapable.plugin("test", function(a,b,callback){
console.log('函数1:'+a+b);
callback();
});
第三步、触发特定的事件(相当于trigger方法)
tapable.applyPluginsAsync("test", '123','qeeqeq', function(){
console.log('cb4');
});
tapable中监听事件的方法就只有一个plugin方法,触发事件的方法有applyPlugins、applyPluginsWaterfall等等,下面详细介绍一下这些触发事件的方法。
3.1 触发事件的分类及详细说明
3.1.1 applyPlugins、applyPlugins0、applyPlugins1、applyPlugins2方法
applyPlugins方法使用说明:
用法:
var tapable = new Tapable();
tapable.applyPlugins('eventname', param1, param2, ...... paramn),
tapable.plugin('eventname', function(param1, param2,...... paramn){})
applyPlugins0使用说明:
源码:
用法:
var tapable = new Tapable();
tapable.applyPlugins0('eventname');
tapable.plugin('eventname', funtion(){});
applyPlugins1的使用说明:
源码:
用法:
var tapable = new Tapable();
tapable.applyPlugins0('eventname', param1);
tapable.plugin('eventname', funtion(param1){});
applyPlugins2的使用说明:
源码:
用法:
var tapable = new Tapable();
tapable.applyPlugins0('eventname', param1, param2);
tapable.plugin('eventname', funtion(param1, param2){});
小结:applyPlugins0、applyPlugins1、applyPlugins2用法与applyPlugins类似,只是监听函数对应的参数不同而已。applyPlugins0对应的监听函数没有参数;applyPlugins1对应的监听函数有一个参数;applyPlugins2对应的监听函数有两个参数。
3.1.2 applyPluginsWaterfall、applyPluginsWaterfall0、applyPluginsWaterfall1、applyPluginsWaterfall2
这组方法的意思是触发某个特定的事件,这个特定的事件会被一系列的流程监听。每个流程处理完后,将结果作为参数传递给下一个监听函数继续处理,直至所有的监听函数执行完毕。最终触发事件的函数获取所有监听函数处理的结果。
applyPluginsWaterfall使用说明:
源码:
用法:
var tapable = new Tapable();
tapable.plugin('eventname', function(processValue, param1, param2,......, paramn){});
tapable.plugin('eventname', function(processValue, param1, param2,......, paramn){});
var result = tapable.applyPluginsWaterfall('eventname', defaultValue, param1, param2,......paramn);
applyPluginsWaterfall0使用说明:
源码:
用法:
var tapable = new Tapable();
tapable.plugin('eventname', function(processValue){});
tapable.plugin('eventname', function(processValue){});
var result = tapable.applyPluginsWaterfall('eventname', defaultValue);
applyPluginsWaterfall1使用说明:
源码:
用法:
var tapable = new Tapable();
tapable.plugin('eventname', function(processValue, param1){});
tapable.plugin('eventname', function(processValue, param1){});
var result = tapable.applyPluginsWaterfall('eventname', defaultValue, param1);
applyPluginsWaterfall2使用说明:
源码:
用法:
var tapable = new Tapable();
tapable.plugin('eventname', function(processValue, param1, param2){});
tapable.plugin('eventname', function(processValue, param1, param2){});
var result = tapable.applyPluginsWaterfall('eventname', defaultValue, param1, param2);
小结:applyPluginsWaterfall系列触发的事件在参数中需要传入默认值(需要被加工的数据),在监听函数处理完后,需要获取默认值被处理完后的结果。
3.1.3 applyPluginsBailResult、applyPluginsBailResult1、applyPluginsBailResult2、applyPluginsBailResult3、applyPluginsBailResult4、applyPluginsBailResult5
这个系列的触发函数需要有返回值,它还有一个特殊之处在监听函数有返回值的情况下,会提前返回。仅以applyPluginsBailResult为例,其他几个方法只是参数不同而已。
源码:
用法:
小结:从执行结果可以看出第二个监听函数返回了结果后,第三次监听就没有执行。最终的返回结果是第二次监听的结果。
3.1.4 applyPluginsAsyncSeries、applyPluginsAsync、applyPluginsAsyncSeries1
仅以applyPluginsAsync为例进行分析
源码:
用法:
小结:从源码和执行结果上看,这组方法的作用是模仿异步的工作流程,在一个监听执行完后,在这个监听的回调函数里面去执行下一个监听函数,类似异步流程。
3.1.5 applyPluginsAsyncSeriesBailResult、applyPluginsAsyncSeriesBailResult1
以applyPluginsAsyncSeriesBailResult为例进行分析
源码:
用法:
从源码和执行结果上分析,applyPluginsAsyncSeriesBailResult函数时以异步的方式进行调用监听函数,但可以提前返回(监听函数的回调函数中有参数)。
3.1.6 applyPluginsAsyncWaterfall
源码:
用法:
小结:applyPluginsAsyncWaterfall方法是以异步的方式串联起监听函数,每个监听函数将前一个函数的处理结果作为参数继续处理,监听函数的整个执行过程就像流水线一样运作。
3.1.7 applyPluginsParallel
源码:
用法:
小结:从源码和运行结果来看,applyPluginsParallel方法触发的方法是并行执行的,三个监听函数最后调用的callback函数(这个callback指的是触发函数执行的回调)与代码的编写顺序无关,与实际结束的时间相关。三个监听函数,最后执行的才会调用触发函数中的回调函数。
3.1.8 applyPluginsParallelBailResult、applyPluginsParallelBailResult1
源码:
用法:
小结:从源码和执行结果分析,applyPluginsParallelBailResult触发的方法是并行执行,与applyPluginsParallel不同的是,触发函数的回调可以提前执行(不必等所有的监听函数执行完毕)。
总结
apply方法用来注册插件。
plugin用来监听具体事件。
applyPlugins、applyPlugins0、applyPlugins1、applyPlugins2;applyPluginsWaterfall、applyPluginsWaterfall0、applyPluginsWaterfall1、applyPluginsWaterfall2;applyPluginsBailResult、applyPluginsBailResult1、applyPluginsBailResult2、applyPluginsBailResult3、applyPluginsBailResult4、applyPluginsBailResult5;applyPluginsAsyncSeries、applyPluginsAsync、applyPluginsAsyncSeries1;applyPluginsAsyncSeriesBailResult、applyPluginsAsyncSeriesBailResult1;applyPluginsAsyncWaterfall;applyPluginsParallel;applyPluginsParallelBailResult、applyPluginsParallelBailResult1 这些方法用来触发具体事件。
触发函数的区别如下表所示:
方法名 | 监听函数的回调函数的最后一个参数是否是函数 | 监听函数执行方式 | 触发函数是否有返回值 | 触发函数的参数中是否有回调函数 | 触发函数的回调函数能否提前执行 |
applyPlugins | 否 | 不涉及 | 无 | 否 | 不涉及 |
applyPluginsWaterfall | 否 | 不涉及 | 有 | 否 | 不涉及 |
applyPluginsBailResult | 否 | 不涉及 | 有 | 否 | 不涉及 |
applyPluginsAsyncSeries | 是 | 异步 | 无 | 有 | 监听函数正常执行不会提前执行 |
applyPluginsAsyncSeriesBailResult | 是 | 异步 | 无 | 有 | 可以 |
applyPluginsAsyncWaterfall | 是 | 异步 | 无 | 有 | 监听函数正常执行不会提前执行 |
applyPluginsParallel | 是 | 同步 | 无 | 有 | 监听函数正常执行不会提前执行 |
applyPluginsParallelBailResult | 是 | 同步 | 无 | 有 | 可以 |
说明:
监听函数指的是:plugin函数
监听函数的回调函数指的是(标红的函数):plugin('eventname', callbackfn())
触发函数指的是:applyPlugins等函数
触发函数的回调函数指的是(标红的函数):tapable.applyPluginsAsync('eventname', params, function(){})