|
Webpack 基于 tapable 构建了其复杂庞大的流程管理系统,基于 tapable 的架构不仅解耦了流程节点和流程的具体实现,还保证了 Webpack 强大的扩展能力;学习掌握tapable,有助于我们深入理解 Webpack。一、tapable是什么?The tapable package expose many Hook classes,which can be used to create hooks for plugins.tapable 提供了一些用于创建插件的钩子类。个人觉得 tapable 是一个基于事件的流程管理工具。二、tapable架构原理和执行过程tapable于2020.9.18发布了v2.0版本。此文章内容也是基于v2.0版本。2.1 代码架构tapable有两个基类:Hook和HookCodeFactory。Hook类定义了Hookinterface(Hook接口),HookCodeFactoruy类的作用是动态生成一个流程控制函数。生成函数的方式是通过我们熟悉的NewFunction(arg,functionBody)。2.2 执行流程tapable会动态生成一个可执行函数来控制钩子函数的执行。我们以SyncHook的使用来举一个例子,比如我们有这样的一段代码:// SyncHook使用import { SyncHook } from '../lib';const syncHook = new SyncHook();syncHook.tap('x', () => console.log('x done'));syncHook.tap('y', () => console.log('y done'));上面的代码只是注册好了钩子函数,要让函数被执行,还需要触发事件(执行调用)syncHook.call();syncHook.call()在调用时会生成这样的一个动态函数:function anonymous() { "use strict"; var _context; var _x = this._x; var _fn0 = _x[0]; _fn0(); var _fn1 = _x[1]; _fn1();}这个函数的代码非常简单:就是从一个数组中取出函数,依次执行。注意:不同的调用方式,最终生成的的动态函数是不同的。如果把调用代码改成:syncHook.callAsync( () => {console.log('all done')} )那么最终生成的动态函数是这样的:function anonymous(_callback) { "use strict"; var _context; var _x = this._x; var _fn0 = _x[0]; var _hasError0 = false; try { _fn0(); } catch(_err) { _hasError0 = true; _callback(_err); } if(!_hasError0) { var _fn1 = _x[1]; var _hasError1 = false; try { _fn1(); } catch(_err) { _hasError1 = true; _callback(_err); } if(!_hasError1) { _callback(); } }}这个动态函数相对于前面的动态函数要复杂一些,但仔细一看,执行逻辑也非常简单:同样是从数组中取出函数,依次执行;只不过这次多了2个逻辑:错误处理在数组中的函数执行完后,执行了回调函数通过研究最终生成的动态函数,我们不难发现:动态函数的模板特性非常突出。前面的例子中,我们只注册了x,y2个钩子,这个模板保证了当我们注册任意个钩子时,动态函数也能方便地生成出来,具有非常强的扩展能力。那么这些动态函数是如何生成的呢?其实Hook的生成流程是一样的。hook.tap只是完成参数准备,真正的动态函数生成是在调用后(水龙头打开后)。完整流程如下:三、Hook 类型详解在tapablev2中,一共提供了12种类型的Hook,接下来,通过梳理Hook怎么执行和Hook完成回调何时执行2方面来理解tapable提供的这些Hook类。3.1 SyncHook钩子函数按次序依次全部执行;如果有Hook回调,则Hook回调在最后执行。const syncHook = new SyncHook();syncHook.tap('x', () => console.log('x done'));syncHook.tap('y', () => console.log('y done'));syncHook.callAsync(() => { console.log('all done') }); /*输出:x doney doneall done*/3.2SyncBailHook钩子函数按次序执行。如果某一步钩子返回了非undefined,则后面的钩子不再执行;如果有Hook回调,直接执行Hook回调。const hook = new SyncBailHook(); hook.tap('x', () => { console.log('x done'); return false; // 返回了非undefined,y不会执行});hook.tap('y', () => console.log('y done'));hook.callAsync(() => { console.log('all done') }); /*输出:x doneall done*/3.3 SyncWaterfallHook钩子函数按次序全部执行。后一个钩子的参数是前一个钩子的返回值。最后执行Hook回调。
|
|