一. tapable概述tapable 地址:【tapable-0.2】
tapable 是webpack 的核心框架(4.0以上版本的API已经发生了变化),是一个基于事件流的框架,或者叫做发布订阅模式,或观察者模式,webpack 的整个生命周期及其开放的自定义插件系统都离不开tapable 的支持,研究其运行原理是阅读webpack 源代码的第一步。官方仓库master 分支的代码是经过ES6重构的,模块化拆分非常细,且加入了很多非核心逻辑,阅读难度较大。建议先从官方仓库中0.2版本的分支开始学习,整个源码只有400行,相对容易理解。
二. tapable-0.2源码解析2.1 代码结构//类定义
function Tapable() {
this._plugins = {};
}
//模块导出
module.exports = Tapable;
//定义了许多内部方法和原型方法
... Tapable 实际上就是一个类定义的模块。
2.2 事件监听方法tapable 通过原型方法Tapable.prototype.plugin 来注册事件监听。

这段代码并不复杂,调用plugin 方法来注册一个事件,参考浏览器环境中的addEventListener() 方法就很容易理解了。其逻辑就是将回调函数按照事件名称进行归类存储,在tapable 实例中统一调度管理。 //__plugin属性上挂载了各个注册事件的回调函数
tapable.__plugins = {
'click':[fn1, fn2, fn3],
'mousedown':[fn21,fn22,fn23]
...
} 2.3 事件触发方法tapable 提供了许多事件触发的方式,其基本功能可以参考浏览器环境中的dispatchEvent( ) 。
tapable 中的事件触发方式可以按命名分为如下几个大组:
waterfall方法会将上一个监听的执行结果传给下一个 bailResult方法只会执行到第一个返回结果不是undefined的监听器 Series方法会线性执行异步监听器,上一个结束后下一个才会开始 Parallel方法会并行执行所有异步监听
tapable 中的典型方法如下:

同步方法,该方法接受任意参数,以第一个参数为事件名查找监听器数组,依次执行监听器的apply( ) 方法,触发时将调用时除名称以外的其他参数传入apply( ) 方法。 
同步方法,该方法接受任意参数,如果指定事件没有注册监听器,则返回第二个参数(init),否则依次执行监听器的apply( ) 方法,传入的args是前一个执行前一个监听器apply( ) 方法的返回值。瀑布流这个方法名很形象。 
同步方法,该方法接受任意参数,依次执行监听器的apply( ) 方法并取得返回值,直到某个apply( ) 返回一个不为undefined 的结果,则停止执行并将这个结果返回。 
异步执行监听回调的方法。这个方法是顺序执行,等到第一个插件执行结束后才会执行下一个插件,实现的方式就是将下一个插件当做回调函数传入第一个插件,在第一个插件的apply( ) 方法的方法体最后(或是异步方法最后)来调用下一个监听插件的执行。这里利用闭包实现了一个迭代器,变量记录在applyPluginsAsync( ) 方法中(就是变量i ),并在回调中函数next( ) 中保持了对i 的引用。 例如需要用applyPluginsAsync( ) 方法执行的插件需要在apply 方法中显式执行回调函数: class Plugin1{
apply(info){
var callback = Array.prototype.pop.call(arguments[1]);
//这里取到的callback,实际上就是源码中的具名函数next()
callback();
}
} 其他的异步方法大同小异,不再赘述。 源码的异步方法定义中使用copyProperties( ) 来处理两个函数,笔者尝试了很多情况这个方法都并未执行,实际情况就是将next函数加入了参数数组并继续执行,希望对此有研究的读者能够点明一下。 三. tapable1.0概述tapable 地址:【tapable-1.0】
tapable 在1.0版本做了很大改进,使用ES6 语法重写了整个框架,除了更换了API外,在插件定义方面进行了明显升级,原来只通过plugin( ) 方法来定义插件,不阅读源码很难知道插件的规范格式,新版本的tapable 提供了基本样例,细分的事件钩子(*Hook),新的触发事件的方法(tap ,tapAsync ,tapPromise )等等,但实现的基本需求是一致的,感兴趣的读者可以自行学习。
作者:大史不说话 链接:Webpack4.0各个击破(8)tapable篇 来源:博客园
|