根据tapable调试结果,手写SyncHook实现

调试过程了解SyncHook案例运行基本原理
let hook = new SyncHook(['name', 'age'])操作构建了一个SyncHook实例,挂载一些属性核心属性是_x和taps属性,最后调用call方法

// SyncHook.js // 非tap模式全部抛出异常 tapAsync、 tapPromise const TAP_ASYNC = () => { throw new Error("tapAsync is not supported on a SyncHook"); }; const TAP_PROMISE = () => { throw new Error("tapPromise is not supported on a SyncHook"); }; function SyncHook(args = [], name = undefined) { const hook = new Hook(args, name); // 挂载属性 hook.constructor = SyncHook; hook.tapAsync = TAP_ASYNC; hook.tapPromise = TAP_PROMISE; hook.compile = COMPILE; return hook; } // Hook.js constructor(args = [], name = undefined) { // 挂载了许多属性 this._args = args; this.name = name; this.taps = []; this.interceptors = []; this._call = CALL_DELEGATE; this.call = CALL_DELEGATE; this._callAsync = CALL_ASYNC_DELEGATE; this.callAsync = CALL_ASYNC_DELEGATE; this._promise = PROMISE_DELEGATE; this.promise = PROMISE_DELEGATE; this._x = undefined; this.compile = this.compile; this.tap = this.tap; this.tapAsync = this.tapAsync; this.tapPromise = this.tapPromise; } _tap(type, options, fn) { if (typeof options === "string") { options = { name: options.trim() }; } else if (typeof options !== "object" || options === null) { throw new Error("Invalid tap options"); } if (typeof options.name !== "string" || options.name === "") { throw new Error("Missing name for tap"); } if (typeof options.context !== "undefined") { deprecateContext(); } // 浅拷贝,并且通过_insert方法赋值给taps数组 options = Object.assign({ type, fn }, options); options = this._runRegisterInterceptors(options); this._insert(options); }

根据核心逻辑,手写实现,源码中一些方法有些谜之操作,可以用一些简便的方式来实现
  1. 实例化 hook , 定义 _x = [f1, f2, ...] taps = [{}, {}]
  2. 实例调用 tap taps = [{}, {}]
  3. 调用 call 方法, HookCodeFactory 类中setup准备数据 create拼接函数
Hook.js
class Hook { // args默认值为空数组 constructor(args = []) { this.args = args this.taps = [] // 用来存储组装好的信息 this._x = undefined // 在代码工厂函数中会给_x =[f1,f2,...]}tap(options, fn) { if (typeof options === "string") { // options组装 options = { name: options } } options = Object.assign({ fn }, options) //{fnL...,name:fn1}// 调用以下方法将组装好的options添加至[] this._insert(options) }_insert(options) { // 源码中有些谜之操作,其实直接复制就可以 this.taps[this.taps.length] = options }_createCall() { return this.compile({ taps: this.taps, args: this.args, }) }call(...args) { // 创建将来要具体执行的函数代码结构 let callFn = this._createCall() // 调用上述函数并且传参args return callFn.apply(this, args) } }module.exports = Hook

SyncHook.js
let Hook = require('./Hook.js')class HookCodeFactory { // 准备后续需要使用到的数据 setup(instance, options) { this.options = options // 源码中通过init实现,这里直接挂载this上 instance._x = options.taps.map(item => item.fn) // fn来自于Hook类挂载的fn }args() { return this.options.args.join(',') // ['name','age'] => name,age } header() { return `var _x = this._x; `; } content() { let code = `` // 循环options.taps for (var i = 0; i < this.options.taps.length; i++) { code += `var _fn${i} = _x[${i}]; _fn${i}(${this.args()}); ` } return code } // 创建一段可执行代码体并返回 create() { let fn // fn = new Function('name,age', 'var _x = this._x,var _fn0=_x[0]; _fn0(name,age); ') 源码中会有代码拼接 fn = new Function(this.args(), this.header() + this.content()) return fn } }let factory = new HookCodeFactory()class SyncHook extends Hook { constructor(args) { super(args) }compile(options) { // optoins结构{taps:[{},{}],args:[name,age]} factory.setup(this, options) return factory.create(options) }}module.exports = SyncHook

测试用例
const SyncHook = require('./SyncHook.js')let hook = new SyncHook(['name', 'age']) hook.tap('fn1', function (name, age) { console.log('fn1', name, age) }) hook.tap('fn2', function (name, age) { console.log('fn2', name, age) })hook.call('jake', 18)

【根据tapable调试结果,手写SyncHook实现】根据tapable调试结果,手写SyncHook实现
文章图片

    推荐阅读