webpack核心模块tapable源码解析 (4)

现在我们的HookCodeFactory只能生成最简单的SyncHook代码,我们需要对他进行一些改进,让他能够也生成SyncBailHook的call函数体。你可以拉回前面再仔细观察下这两个最终生成代码的区别:

SyncBailHook需要对每次执行的result进行处理,如果不为undefined就返回

SyncBailHook生成的代码其实是if...else嵌套的,我们生成的时候可以考虑使用一个递归函数

为了让SyncHook和SyncBailHook的子类代码工厂能够传入差异化的result处理,我们先将HookCodeFactory基类的create拆成两部分,将代码拼装的逻辑单独拆成一个函数:

class HookCodeFactory { // ... // 省略其他一样的代码 // ... // create创建最终的call函数 create(options) { this.init(options); let fn; // 拼装代码头部 const header = ` "use strict"; var _x = this._x; `; // 用传进来的参数和函数体创建一个函数出来 fn = new Function(this.args(), header + this.content()); // 注意这里的content函数并没有在基类HookCodeFactory实现,而是子类实现的 this.deinit(); return fn; } // 拼装函数体 // callTapsSeries也没在基类调用,而是子类调用的 callTapsSeries() { const { taps } = this.options; let code = ''; for (let i = 0; i < taps.length; i++) { code += ` var _fn${i} = _x[${i}]; _fn${i}(${this.args()}); ` } return code; } }

上面代码里面要特别注意create函数里面生成函数体的时候调用的是this.content,但是this.content并没与在基类实现,这要求子类在使用HookCodeFactory的时候都需要继承他并实现自己的content函数,所以这里的content函数也是一个抽象接口。那SyncHook的代码就应该改成这样:

// SyncHook.js // ... 省略其他一样的代码 ... // SyncHookCodeFactory继承HookCodeFactory并实现content函数 class SyncHookCodeFactory extends HookCodeFactory { content() { return this.callTapsSeries(); // 这里的callTapsSeries是基类的 } } // 使用SyncHookCodeFactory来创建factory const factory = new SyncHookCodeFactory(); const COMPILE = function (options) { factory.setup(this, options); return factory.create(options); };

注意这里:子类实现的content其实又调用了基类的callTapsSeries来生成最终的函数体。所以这里这几个函数的调用关系其实是这样的:

image-20210401111739814

那这样设计的目的是什么呢为了让子类content能够传递参数给基类callTapsSeries,从而生成不一样的函数体。我们马上就能在SyncBailHook的代码工厂上看到了。

为了能够生成SyncBailHook的函数体,我们需要让callTapsSeries支持一个onResult参数,就是这样:

class HookCodeFactory { // ... 省略其他相同的代码 ... // 拼装函数体,需要支持options.onResult参数 callTapsSeries(options) { const { taps } = this.options; let code = ''; let i = 0; const onResult = options && options.onResult; // 写一个next函数来开启有onResult回调的函数体生成 // next和onResult相互递归调用来生成最终的函数体 const next = () => { if(i >= taps.length) return ''; const result = `_result${i}`; const code = ` var _fn${i} = _x[${i}]; var ${result} = _fn${i}(${this.args()}); ${onResult(i++, result, next)} `; return code; } // 支持onResult参数 if(onResult) { code = next(); } else { // 没有onResult参数的时候,即SyncHook跟之前保持一样 for(; i< taps.length; i++) { code += ` var _fn${i} = _x[${i}]; _fn${i}(${this.args()}); ` } } return code; } }

然后我们的SyncBailHook的代码工厂在继承工厂基类的时候需要传一个onResult参数,就是这样:

const Hook = require('./Hook'); const HookCodeFactory = require("./HookCodeFactory"); // SyncBailHookCodeFactory继承HookCodeFactory并实现content函数 // content里面传入定制的onResult函数,onResult回去调用next递归生成嵌套的if...else... class SyncBailHookCodeFactory extends HookCodeFactory { content() { return this.callTapsSeries({ onResult: (i, result, next) => `if(${result} !== undefined) {\nreturn ${result};\n} else {\n${next()}}\n`, }); } } // 使用SyncHookCodeFactory来创建factory const factory = new SyncBailHookCodeFactory(); const COMPILE = function (options) { factory.setup(this, options); return factory.create(options); }; function SyncBailHook(args = []) { // 基本结构跟SyncHook都是一样的 const hook = new Hook(args); hook.constructor = SyncBailHook; // 使用HookCodeFactory来创建最终的call函数 hook.compile = COMPILE; return hook; }

现在运行下代码,效果跟之前一样的,大功告成~

其他Hook的实现

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wsfpxj.html