webpack
做为前端最火的构建工具,是前端自动化工具链最重要的部分,使用门槛较高。本系列是笔者本身的学习记录,比较基础,但愿经过问题 + 解决方式的模式,之前端构建中遇到的具体需求为出发点,学习webpack
工具中相应的处理办法。(本篇中的参数配置及使用方式均基于webpack4.0版本
)前端
tapable
地址:【tapable-0.2】webpack
tapable
是webpack
的核心框架(4.0以上版本的API已经发生了变化),是一个基于事件流的框架,或者叫作发布订阅模式,或观察者模式,webpack
的整个生命周期及其开放的自定义插件系统都离不开tapable
的支持,研究其运行原理是阅读webpack
源代码的第一步。官方仓库master
分支的代码是通过ES6重构的,模块化拆分很是细,且加入了不少非核心逻辑,阅读难度较大。建议先从官方仓库中0.2版本的分支开始学习,整个源码只有400行,相对容易理解。git
//类定义 function Tapable() { this._plugins = {}; } //模块导出 module.exports = Tapable; //定义了许多内部方法和原型方法 ...
Tapable
实际上就是一个类定义的模块。github
tapable
经过原型方法Tapable.prototype.plugin
来注册事件监听。web
这段代码并不复杂,调用plugin
方法来注册一个事件,参考浏览器环境中的addEventListener()
方法就很容易理解了。其逻辑就是将回调函数按照事件名称进行归类存储,在tapable
实例中统一调度管理。数组
//__plugin属性上挂载了各个注册事件的回调函数 tapable.__plugins = { 'click':[fn1, fn2, fn3], 'mousedown':[fn21,fn22,fn23] ... }
tapable
提供了许多事件触发的方式,其基本功能能够参考浏览器环境中的dispatchEvent( )
。浏览器
tapable
中的事件触发方式能够按命名分为以下几个大组:闭包
tapable
中的典型方法以下:app
Tapable.prototype.applyPlugins( )
同步方法,该方法接受任意参数,以第一个参数为事件名查找监听器数组,依次执行监听器的apply( )
方法,触发时将调用时除名称之外的其余参数传入apply( )
方法。框架
Tapable.prototype.applyPluginsWaterfall( )
同步方法,该方法接受任意参数,若是指定事件没有注册监听器,则返回第二个参数(init),不然依次执行监听器的apply( )
方法,传入的args是前一个执行前一个监听器apply( )
方法的返回值。瀑布流这个方法名很形象。
Tapable.prototype.applyPluginsBailResult( )
同步方法,该方法接受任意参数,依次执行监听器的apply( )
方法并取得返回值,直到某个apply( )
返回一个不为undefined
的结果,则中止执行并将这个结果返回。
Tapable.prototype.applyPluginsAsync( )
异步执行监听回调的方法。这个方法是顺序执行,等到第一个插件执行结束后才会执行下一个插件,实现的方式就是将下一个插件当作回调函数传入第一个插件,在第一个插件的apply( )
方法的方法体最后(或是异步方法最后)来调用下一个监听插件的执行。这里利用闭包实现了一个迭代器,变量记录在applyPluginsAsync( )
方法中(就是变量i
),并在回调中函数next( )
中保持了对i
的引用。
例如须要用applyPluginsAsync( )
方法执行的插件须要在apply
方法中显式执行回调函数:
class Plugin1{ apply(info){ var callback = Array.prototype.pop.call(arguments[1]); //这里取到的callback,实际上就是源码中的具名函数next() callback(); } }
其余的异步方法大同小异,再也不赘述。
源码的异步方法定义中使用copyProperties( )
来处理两个函数,笔者尝试了不少状况这个方法都并未执行,实际状况就是将next函数加入了参数数组并继续执行,但愿对此有研究的读者可以点明一下。
tapable
地址:【tapable-1.0】
tapable
在1.0版本作了很大改进,使用ES6
语法重写了整个框架,除了更换了API外,在插件定义方面进行了明显升级,原来只经过plugin( )
方法来定义插件,不阅读源码很难知道插件的规范格式,新版本的tapable
提供了基本样例,细分的事件钩子(*Hook),新的触发事件的方法(tap
,tapAsync
,tapPromise
)等等,但实现的基本需求是一致的,感兴趣的读者能够自行学习。