在tapable中,有两个基本的基类
- Hook
- HookCodeFactory
SyncHook、 SyncWaterHooks等所有的hooks都继承Hook基类。
HookCodeFactory的子类包括SyncHookCodeFactory、 SyncWaterCodeFactory 等不同的工厂构造函数,可以实例化出来不同工厂实例factory。
这个工厂实例factory的作用是,拼接生产出不同的 compile 函数,生产 compile 函数的过程,本质上就是拼接字符串,生成执行的代码。compile 函数,最终会在 call() 方法被调用。
生成代码依赖条件如下:
- 注册的插件数量(0,一个,多个)
- 注册的插件的种类(sync, async, promise)
- 调用方法(sync, async, promise)
- 参数的个数
- 是否有interception
实例化
要使用hook要先进行实例化,而特定的hook继承自Hook类,所有的hook都是如此。
如SyncHook
class SyncHook extends Hook {
tapAsync() {
throw new Error("tapAsync is not supported on a SyncHook");
}
tapPromise() {
throw new Error("tapPromise is not supported on a SyncHook");
}
compile(options) {
factory.setup(this, options);
return factory.create(options);
}
}
先看基类Hook
class Hook {
constructor(args) {
if (!Array.isArray(args)) args = [];
this._args = args;// 事件回调的接收参数名数组
this.taps = [];//注册的事件对象(事件名,回调等)
this.interceptors = [];//拦截器
//三个触发事件的方法
this.call = this._call;
this.promise = this._promise;
this.callAsync = this._callAsync;
//存放回调函数,拼接代码时使用
this._x = undefined;
}
// 根据特定的策略编译执行插件的代码
compile(options) {
throw new Error("Abstract: should be overriden");
}
_createCall(type) {
return this.compile({
taps: this.taps,
interceptors: this.interceptors,
args: this._args,
type: type
});
}
}
事件注册
有三种注册方式
tap(options, fn) {
//options注册事件的名字或者其他参数,可以是字符串或者对象,最终会被处理为对象。
if (typeof options === "string") options = { name: options };
if (typeof options !== "object" || options === null)
throw new Error(
"Invalid arguments to tap(options: Object, fn: function)"
);
//type属性,不同的监听方式type不同
//在之后的处理时会根据 type 的不同触发不同的逻辑。
options = Object.assign({ type: "sync", fn: fn }, options);
if (typeof options.name !== "string" || options.name === "")
throw new Error("Missing name for tap");
options = this._runRegisterInterceptors(options);
this._insert(options);
}
tapAsync(options, fn) {
if (typeof options === "string") options = { name: options };
if (typeof options !== "object" || options === null)
throw new Error(
"Invalid arguments to tapAsync(options: Object, fn: function)"
);
options = Object.assign({ type: "async", fn: fn }, options);
if (typeof options.name !== "string" || options.name === "")
throw new Error("Missing name for tapAsync");
options = this._runRegisterInterceptors(options);
this._insert(options);
}
tapPromise(options, fn) {
if (typeof options === "string") options = { name: options };
if (typeof options !== "object" || options === null)
throw new Error(
"Invalid arguments to tapPromise(options: Object, fn: function)"
);
options = Object.assign({ type: "promise", fn: fn }, options);
if (typeof options.name !== "string" || options.name === "")
throw new Error("Missing name for tapPromise");
options = this._runRegisterInterceptors(options);
this._insert(options);
}
// 处理拦截器register属性
_runRegisterInterceptors(options) {
for (const interceptor of this.interceptors) {
if (interceptor.register) {
const newOptions = interceptor.register(options);
if (newOptions !== undefined) options = newOptions;
}
}
return options;
}
isUsed() {
return this.taps.length > 0 || this.interceptors.length > 0;
}
//重置三个调用事件的方法
_resetCompilation() {
this.call = this._call;
this.callAsync = this._callAsync;
this.promise = this._promise;
}
// 根据before,stage属性排列监听事件的顺序
_insert(item) {
this._resetCompilation();
let before;
// 如果 before 属性存在,把它转换成 Set 数据结构
if (typeof item.before === "string") before = new Set([item.before]);
else if (Array.isArray(item.before)) {
before = new Set(item.before);
}
let stage = 0;
if (typeof item.stage === "number") stage = item.stage;
let i = this.taps.length;
while (i > 0) {
i--;
//每次遍历,taps 数组中每次都会存在相邻的两个相同的值。循环结束就能找到要替换的下标。
const x = this.taps[i];
this.taps[i + 1] = x;
const xStage = x.stage || 0;
// 如果碰到传入 before 中有当前 name 的,就继续遍历,直到把 before 全部清空。
if (before) {
if (before.has(x.name)) {
before.delete(x.name);
continue;
}
// 如果 before 中的值没被删干净,
// 新加入的事件回调最终会在最前面执行
if (before.size > 0) {
continue;
}
}
// 如果当前 stage 大于传入的 stage,那么继续遍历
if (xStage > stage) {
continue;
}
i++;
break;
}
// 循环结束的时候 i 就是是要赋值的那个下标
this.taps[i] = item;
}
触发
三种触发方式
在Hook的构造器中对这三个方法进行了赋值操作。
this.call = this._call;
this.promise = this._promise;
this.callAsync = this._callAsync;
this._call,this._promise,this._callAsync,都是通过Object.defineProperties对Hook.prototype进行了重新的赋值。
Object.defineProperties(Hook.prototype, {
_call: {
value: createCompileDelegate("call", "sync"),
configurable: true,
writable: true
},
_promise: {
value: createCompileDelegate("promise", "promise"),
configurable: true,
writable: true
},
_callAsync: {
value: createCompileDelegate("callAsync", "async"),
configurable: true,
writable: true
}
});
_这种方式很方便将_call方法赋值为另一个函数,而且可以直接在子类(如AsyncParallelHook)中,再利用Object.defineProperties将_call的vale赋值为null。就可以得到一个没有_call方法的子类。
promise和callAsync同理。
Object.defineProperties(Hook.prototype, {
_call: {
value: createCompileDelegate("call", "sync"),
configurable: true,
writable: true
},
_promise: {
value: createCompileDelegate("promise", "promise"),
configurable: true,
writable: true
},
_callAsync: {
value: createCompileDelegate("callAsync", "async"),
configurable: true,
writable: true
}
});
createCompileDelegate
function createCompileDelegate(name, type) {
return function lazyCompileHook(...args) {
this[name] = this._createCall(type);
return this[name](...args);
};
}
平时调用call等方法就是调用lazyCompileHook,通过闭包保存了name,和type。
以call方法为例,调用call时,先执行this._createCall() 方法,把返回值赋值给 this[name] ,然后执行this[name]方法。
即
this.call = this._createCall(type)
第二次及之后执行 call 方法会直接执行已经编译好的静态脚本,这里用到了惰性函数来优化代码的运行性能
看到这里也可以解释_insert方法里 this._resetCompilation() 这个操作了,因为执行 call 方法时执行的是编译好的静态脚本,在添加监听函数是如果不重置,可能执行的静态脚本就不会包含当前注册的事件回调。
惰性函数优点
- 效率高:惰性函数仅在第一次运行时执行计算逻辑,之后函数再次运行时都返回第一次执行的结果,节约了很多执行时间
- 延迟执行
this._createCall() 里面本质是调用了this.compiler 方法,但是基类Hook上的compiler() 方法是一个空实现。这个方法是子类实现的。
compile
class SyncHookCodeFactory extends HookCodeFactory {
content({ onError, onDone, rethrowIfPossible }) {
return this.callTapsSeries({
onError: (i, err) => onError(err),
onDone,
rethrowIfPossible
});
}
}
const factory = new SyncHookCodeFactory();
class SyncHook extends Hook {
tapAsync() {
throw new Error("tapAsync is not supported on a SyncHook");
}
tapPromise() {
throw new Error("tapPromise is not supported on a SyncHook");
}
compile(options) {
factory.setup(this, options);
return factory.create(options);
}
}
module.exports = SyncHook;
compile 方法中去,它内部使用到了 factory 变量,这个变量是实例化 SyncHookCodeFactory 类的结果,而 SyncHookCodeFactory 类继承自 HookCodeFactory 类。
在compile中调用了factory的setup 和 create方法,这两个方法都来自父类HookCodeFactory
class HookCodeFactory {
...
// factory.setup(this, options);
// 注释中的 this 都是 SyncHook 的实例
// instance = this
// options = {
// taps: this.taps,
// interceptors: this.interceptors,
// args: this._args,
// type: type
// }
setup(instance, options) {
instance._x = options.taps.map(t => t.fn);
}
...
}
instance 接收的是 SyncHook 的实例,options 接收的是 Hook 类中_createCall 方法中传入的对象。
setup方法职责就是把options对象中回调函数数组保存到_x属性上。_x 属性会在静态脚本内部会发挥重要作用。
class HookCodeFactory {
constructor(config) {
this.config = config;
this.options = undefined;
this._args = undefined;
}
create(options) {
this.init(options);
let fn;
switch (this.options.type) {
case "sync":
fn = new Function(
this.args(),
'"use strict";\n' +
this.header() +
this.content({
onError: err => `throw ${err};\n`,
onResult: result => `return ${result};\n`,
resultReturns: true,
onDone: () => "",
rethrowIfPossible: true
})
);
break;
case "async":
fn = new Function(
this.args({
after: "_callback"
}),
'"use strict";\n' +
this.header() +
this.content({
onError: err => `_callback(${err});\n`,
onResult: result => `_callback(null, ${result});\n`,
onDone: () => "_callback();\n"
})
);
break;
case "promise":
let errorHelperUsed = false;
const content = this.content({
onError: err => {
errorHelperUsed = true;
return `_error(${err});\n`;
},
onResult: result => `_resolve(${result});\n`,
onDone: () => "_resolve();\n"
});
let code = "";
code += '"use strict";\n';
code += "return new Promise((_resolve, _reject) => {\n";
if (errorHelperUsed) {
code += "var _sync = true;\n";
code += "function _error(_err) {\n";
code += "if(_sync)\n";
code += "_resolve(Promise.resolve().then(() => { throw _err; }));\n";
code += "else\n";
code += "_reject(_err);\n";
code += "};\n";
}
code += this.header();
code += content;
if (errorHelperUsed) {
code += "_sync = false;\n";
}
code += "});\n";
fn = new Function(this.args(), code);
break;
}
this.deinit();
return fn;
}
setup(instance, options) {
instance._x = options.taps.map(t => t.fn);
}
/**
* @param {{ type: "sync" | "promise" | "async", taps: Array<Tap>, interceptors: Array<Interceptor> }} options
*/
init(options) {
this.options = options;
this._args = options.args.slice();
}
deinit() {
this.options = undefined;
this._args = undefined;
}
header() {
let code = "";
if (this.needContext()) {
code += "var _context = {};\n";
} else {
code += "var _context;\n";
}
code += "var _x = this._x;\n";
if (this.options.interceptors.length > 0) {
code += "var _taps = this.taps;\n";
code += "var _interceptors = this.interceptors;\n";
}
for (let i = 0; i < this.options.interceptors.length; i++) {
const interceptor = this.options.interceptors[i];
if (interceptor.call) {
code += `${this.getInterceptor(i)}.call(${this.args({
before: interceptor.context ? "_context" : undefined
})});\n`;
}
}
return code;
}
needContext() {
for (const tap of this.options.taps) if (tap.context) return true;
return false;
}
....省略代码
//拼接参数
args({ before, after } = {}) {
let allArgs = this._args;
if (before) allArgs = [before].concat(allArgs);
if (after) allArgs = allArgs.concat(after);
if (allArgs.length === 0) {
return "";
} else {
return allArgs.join(", ");
}
}
getTapFn(idx) {
return `_x[${idx}]`;
}
getTap(idx) {
return `_taps[${idx}]`;
}
getInterceptor(idx) {
return `_interceptors[${idx}]`;
}
}
这里关键是通过 new Function 来生成代码。
new Function 第一个参数是函数的参数,第二个参数是函数体。
通过this.args方法拼接参数,ths.args 方法接收一个对象,对象中有 before 和 after,before 主要用于拼接 context,after 主要用于拼接回调函数(例如 callAsync 传入的回调函数)。
在生成函数体的时候调用了header方法和content方法。
header方法主要是申明一些变量,如context,_x,_taps等,在header方法中有一个this, 因为 new Function 生成的函数最终是赋值给 SyncHook 类的实例的 call 方法,所以是指向SyncHook 类的实例的。
content方法是在子类上定义的,它要是实现各个hooks的差异化代码。
根据不同的类型,content方法有不同的实现,目的是。让子类自己去写拼接的逻辑。
//syncHook
class SyncHookCodeFactory extends HookCodeFactory {
content({ onError, onResult, onDone, rethrowIfPossible }) {
return this.callTapsSeries({
onError: (i, err) => onError(err),
onDone,
rethrowIfPossible
});
}
}
//syncBailHook
content({ onError, onResult, onDone, rethrowIfPossible }) {
return this.callTapsSeries({
onError: (i, err) => onError(err),
onResult: (i, result, next) => `if(${result} !== undefined) {\n${onResult(result)};\n} else {\n${next()}}\n`,
onDone,
rethrowIfPossible
});
}
//AsyncSeriesLoopHook
class AsyncSeriesLoopHookCodeFactory extends HookCodeFactory {
content({ onError, onDone }) {
return this.callTapsLooping({
onError: (i, err, next, doneBreak) => onError(err) + doneBreak(true),
onDone
});
}
}
.......
在所有的子类中,都实现了 content 方法,根据不同钩子执行流程的不同,调用了 callTapsSeries/callTapsParallel/callTapsLooping 并且会有 onError, onResult, onDone, rethrowIfPossible 这四中情况下的代码片段,callTapsSeries/callTapsParallel/callTapsLooping 都在基类的方法中,这三个方法中都会走到一个 callTap 的方法。
callTap
callTap(tapIndex, { onError, onResult, onDone, rethrowIfPossible }) {
let code = "";
let hasTapCached = false;
//interceptors处理逻辑
for (let i = 0; i < this.options.interceptors.length; i++) {
const interceptor = this.options.interceptors[i];
if (interceptor.tap) {
if (!hasTapCached) {
code += `var _tap${tapIndex} = ${this.getTap(tapIndex)};\n`;
hasTapCached = true;
}
code += `${this.getInterceptor(i)}.tap(${
interceptor.context ? "_context, " : ""
}_tap${tapIndex});\n`;
}
}
//获取当前的监听函数
code += `var _fn${tapIndex} = ${this.getTapFn(tapIndex)};\n`;
const tap = this.options.taps[tapIndex];
// 分为sync/async/promise类型
switch (tap.type) {
case "sync":
if (!rethrowIfPossible) {
code += `var _hasError${tapIndex} = false;\n`;
code += "try {\n";
}
if (onResult) {
code += `var _result${tapIndex} = _fn${tapIndex}(${this.args({
before: tap.context ? "_context" : undefined
})});\n`;
} else {
code += `_fn${tapIndex}(${this.args({
before: tap.context ? "_context" : undefined
})});\n`;
}
if (!rethrowIfPossible) {
code += "} catch(_err) {\n";
code += `_hasError${tapIndex} = true;\n`;
code += onError("_err");
code += "}\n";
code += `if(!_hasError${tapIndex}) {\n`;
}
if (onResult) {
code += onResult(`_result${tapIndex}`);
}
if (onDone) {
code += onDone();
}
if (!rethrowIfPossible) {
code += "}\n";
}
break;
case "async":
let cbCode = "";
if (onResult) cbCode += `(_err${tapIndex}, _result${tapIndex}) => {\n`;
else cbCode += `_err${tapIndex} => {\n`;
cbCode += `if(_err${tapIndex}) {\n`;
cbCode += onError(`_err${tapIndex}`);
cbCode += "} else {\n";
if (onResult) {
cbCode += onResult(`_result${tapIndex}`);
}
if (onDone) {
cbCode += onDone();
}
cbCode += "}\n";
cbCode += "}";
code += `_fn${tapIndex}(${this.args({
before: tap.context ? "_context" : undefined,
after: cbCode
})});\n`;
break;
case "promise":
code += `var _hasResult${tapIndex} = false;\n`;
code += `var _promise${tapIndex} = _fn${tapIndex}(${this.args({
before: tap.context ? "_context" : undefined
})});\n`;
code += `if (!_promise${tapIndex} || !_promise${tapIndex}.then)\n`;
code += ` throw new Error('Tap function (tapPromise) did not return promise (returned ' + _promise${tapIndex} + ')');\n`;
code += `_promise${tapIndex}.then(_result${tapIndex} => {\n`;
code += `_hasResult${tapIndex} = true;\n`;
if (onResult) {
code += onResult(`_result${tapIndex}`);
}
if (onDone) {
code += onDone();
}
code += `}, _err${tapIndex} => {\n`;
code += `if(_hasResult${tapIndex}) throw _err${tapIndex};\n`;
code += onError(`_err${tapIndex}`);
code += "});\n";
break;
}
return code;
}
callTap 内是一次函数执行的模板,也是根据调用方式的不同,分为 sync/async/promise 三种。
callTapsSeries
callTapsSeries({
onError,
onResult,
resultReturns,
onDone,
doneReturns,
rethrowIfPossible
}) {
if (this.options.taps.length === 0) return onDone();
const firstAsync = this.options.taps.findIndex(t => t.type !== "sync");
const somethingReturns = resultReturns || doneReturns || false;
let code = "";
let current = onDone;
for (let j = this.options.taps.length - 1; j >= 0; j--) {
const i = j;
//异步类型使用
const unroll = current !== onDone && this.options.taps[i].type !== "sync";
if (unroll) {
code += `function _next${i}() {\n`;
code += current();
code += `}\n`;
current = () => `${somethingReturns ? "return " : ""}_next${i}();\n`;
}
//保存上次循环的结果字符串
const done = current;
const doneBreak = skipDone => {
if (skipDone) return "";
return onDone();
};
const content = this.callTap(i, {
onError: error => onError(i, error, done, doneBreak),
onResult:
onResult &&
(result => {
return onResult(i, result, done, doneBreak);
}),
onDone: !onResult && done,
rethrowIfPossible:
rethrowIfPossible && (firstAsync < 0 || i < firstAsync)
});
current = () => content;
}
code += current();
return code;
}
this.callTap 中 onResult 和 onDone 的条件,就是说要么执行 onResult, 要么执行 onDone。
以SyncHook为例,
hook.tap({
name: 'first',
}, (params) => {
console.log('first')
})
hook.tap({
name: 'second',
before: 'first',
}, (params) => {
console.log('second')
})
hook.tap({
name: 'third',
stage: -1
}, (params) => {
console.log('third')
})
hook.call('tapable', 'siven')
其生成的代码如下:
(function anonymous(SyncHook
) {
"use strict";
var _context;
var _x = this._x;
var _fn0 = _x[0];
_fn0(SyncHook);
var _fn1 = _x[1];
_fn1(SyncHook);
var _fn2 = _x[2];
_fn2(SyncHook);
})
callTapsLooping
循环执行的钩子会执行callTapsLooping。
callTapsLooping({ onError, onDone, rethrowIfPossible }) {
if (this.options.taps.length === 0) return onDone();
const syncOnly = this.options.taps.every(t => t.type === "sync");
let code = "";
if (!syncOnly) {
code += "var _looper = () => {\n";
code += "var _loopAsync = false;\n";
}
code += "var _loop;\n";
code += "do {\n";
code += "_loop = false;\n";
for (let i = 0; i < this.options.interceptors.length; i++) {
const interceptor = this.options.interceptors[i];
if (interceptor.loop) {
code += `${this.getInterceptor(i)}.loop(${this.args({
before: interceptor.context ? "_context" : undefined
})});\n`;
}
}
code += this.callTapsSeries({
onError,
onResult: (i, result, next, doneBreak) => {
let code = "";
code += `if(${result} !== undefined) {\n`;
code += "_loop = true;\n";
if (!syncOnly) code += "if(_loopAsync) _looper();\n";
code += doneBreak(true);
code += `} else {\n`;
code += next();
code += `}\n`;
return code;
},
onDone:
onDone &&
(() => {
let code = "";
code += "if(!_loop) {\n";
code += onDone();
code += "}\n";
return code;
}),
rethrowIfPossible: rethrowIfPossible && syncOnly
});
code += "} while(_loop);\n";
if (!syncOnly) {
code += "_loopAsync = true;\n";
code += "};\n";
code += "_looper();\n";
}
return code;
}
例子
hook.tap({
name: 'first',
}, (params) => {
console.log(params);
console.log('first');
})
hook.tap({
name: 'second',
}, (params) => {
console.log(params);
console.log('second');
count ++;
if(count>1){
return;
}
return true
})
hook.tap({
name: 'third',
}, (params) => {
console.log('third');
})
hook.call('tapable', 'siven');
生成代码如下:
(function anonymous(SyncLoopHook) {
"use strict";
var _context;
var _x = this._x;
var _loop;
do {
_loop = false;
var _fn0 = _x[0];
var _result0 = _fn0(SyncLoopHook);
if (_result0 !== undefined) {
_loop = true;
} else {
var _fn1 = _x[1];
var _result1 = _fn1(SyncLoopHook);
if (_result1 !== undefined) {
_loop = true;
} else {
var _fn2 = _x[2];
var _result2 = _fn2(SyncLoopHook);
if (_result2 !== undefined) {
_loop = true;
} else {
if (!_loop) {}
}
}
}
} while (_loop);
}
)
callTapsParallel
callTapsParallel({
onError,
onResult,
onDone,
rethrowIfPossible,
onTap = (i, run) => run()
}) {
if (this.options.taps.length <= 1) {
return this.callTapsSeries({
onError,
onResult,
onDone,
rethrowIfPossible
});
}
let code = "";
code += "do {\n";
code += `var _counter = ${this.options.taps.length};\n`;
if (onDone) {
code += "var _done = () => {\n";
code += onDone();
code += "};\n";
}
for (let i = 0; i < this.options.taps.length; i++) {
const done = () => {
if (onDone) return "if(--_counter === 0) _done();\n";
else return "--_counter;";
};
const doneBreak = skipDone => {
if (skipDone || !onDone) return "_counter = 0;\n";
else return "_counter = 0;\n_done();\n";
};
code += "if(_counter <= 0) break;\n";
code += onTap(
i,
() =>
this.callTap(i, {
onError: error => {
let code = "";
code += "if(_counter > 0) {\n";
code += onError(i, error, done, doneBreak);
code += "}\n";
return code;
},
onResult:
onResult &&
(result => {
let code = "";
code += "if(_counter > 0) {\n";
code += onResult(i, result, done, doneBreak);
code += "}\n";
return code;
}),
onDone:
!onResult &&
(() => {
return done();
}),
rethrowIfPossible
}),
done,
doneBreak
);
}
code += "} while(false);\n";
return code;
}
AsyncParallelHook例子
const hook = new AsyncParallelHook(["name"]);
console.time('cost');
hook.tapAsync("first", (name, callback) => {
setTimeout(() => {
console.log('first');
callback();
}, 1000);
});
hook.tapAsync("second", (name, callback) => {
setTimeout(() => {
console.log('second');
callback();
}, 2000);
});
hook.callAsync('tapable',(error, result) => {
console.log("end", error, result);
console.timeEnd('cost');
});
(function anonymous(name, _callback) {
"use strict";
var _context;
var _x = this._x;
do {
var _counter = 2;
var _done = ()=>{
_callback();
}
;
if (_counter <= 0)
break;
var _fn0 = _x[0];
_fn0(name, _err0=>{
//这个函数是 next 函数
//调用这个函数的时间不能确定,有可能已经执行了接下来的几个注册函数
if (_err0) {
//如果还没执行所有注册函数,终止
if (_counter > 0) {
_callback(_err0);
_counter = 0;
}
} else {
//检查 _counter 的值,如果是 0 的话,则结束
//同样,由于函数实际调用时间无法确定,需要检查是否已经运行完毕
if (--_counter === 0)
_done();
}
}
);
if (_counter <= 0)
break;
var _fn1 = _x[1];
_fn1(name, _err1=>{
if (_err1) {
if (_counter > 0) {
_callback(_err1);
_counter = 0;
}
} else {
if (--_counter === 0)
_done();
}
}
);
} while (false);
}
)
总结一下 callTap 中实现了 sync/promise/async 三种基本的一次函数执行的模板,同时将涉及函数执行流程的代码 onError/onDone/onResult 部分抽离出来, 在 callTapsSeries/callTapsLooping/callTapsParallel 中,通过传入不同的 onError/onDone/onResult 实现出不同流程的模板 。
生成代码之后便是把 new Function 生成的函数返回给 call 方法,然后让 call 方法去执行静态脚本了。
其他hooks生成代码示例。
hook.tap({
name: 'first',
}, (params) => {
console.log(params);
return 'first';
})
hook.tap({
name: 'second',
}, (params) => {
console.log(params);
return 'second';
})
hook.tap({
name: 'third',
}, (params) => {
console.log(params);
return 'third';
})
hook.call('tapable', 'siven')
<!--生成代码:-->
(function anonymous(SyncWaterfallHook) {
"use strict";
var _context;
var _x = this._x;
var _fn0 = _x[0];
var _result0 = _fn0(SyncWaterfallHook);
if (_result0 !== undefined) {
SyncWaterfallHook = _result0;
}
var _fn1 = _x[1];
var _result1 = _fn1(SyncWaterfallHook);
if (_result1 !== undefined) {
SyncWaterfallHook = _result1;
}
var _fn2 = _x[2];
var _result2 = _fn2(SyncWaterfallHook);
if (_result2 !== undefined) {
SyncWaterfallHook = _result2;
}
return SyncWaterfallHook;
}
)
SyncBailHook
hook.tap({
name: 'first',
}, (params) => {
console.log('first')
})
hook.tap({
name: 'second',
}, (params) => {
console.log('second');
return 'second return';
})
hook.tap({
name: 'third',
}, (params) => {
console.log('third')
})
hook.call('tapable', 'siven')
(function anonymous(SyncBailHook) {
"use strict";
var _context;
var _x = this._x;
var _fn0 = _x[0];
var _result0 = _fn0(SyncBailHook);
if(_result0 !== undefined) {
return _result0;
} else {
var _fn1 = _x[1];
var _result1 = _fn1(SyncBailHook);
if(_result1 !== undefined) {
return _result1;
} else {
var _fn2 = _x[2];
var _result2 = _fn2(SyncBailHook);
if(_result2 !== undefined) {
return _result2;
} else {
}
}
}
})
AsyncSeriesHook
hook.tapAsync("first", (name, callback) => {
console.log("first", name);
callback();
});
hook.tapAsync("second", (name, callback) => {
console.log("second", name);
callback("second error", "second result");
});
hook.tapAsync("third", (name, callback) => {
console.log("third", name);
callback(null,"third");
});
hook.callAsync("callAsync", (error, result) => {
console.log("callAsync", error, result);
});
<!--生成代码-->
(function anonymous(name, _callback
) {
"use strict";
var _context;
var _x = this._x;
function _next1() {
var _fn2 = _x[2];
_fn2(name, _err2 => {
if(_err2) {
_callback(_err2);
} else {
_callback();
}
});
}
function _next0() {
var _fn1 = _x[1];
_fn1(name, _err1 => {
if(_err1) {
_callback(_err1);
} else {
_next1();
}
});
}
var _fn0 = _x[0];
_fn0(name, _err0 => {
if(_err0) {
_callback(_err0);
} else {
_next0();
}
});
})
在tapable中,有两个基本的基类
SyncHook、 SyncWaterHooks等所有的hooks都继承Hook基类。
HookCodeFactory的子类包括SyncHookCodeFactory、 SyncWaterCodeFactory 等不同的工厂构造函数,可以实例化出来不同工厂实例factory。
这个工厂实例factory的作用是,拼接生产出不同的 compile 函数,生产 compile 函数的过程,本质上就是拼接字符串,生成执行的代码。compile 函数,最终会在 call() 方法被调用。
生成代码依赖条件如下:
实例化
要使用hook要先进行实例化,而特定的hook继承自Hook类,所有的hook都是如此。
如SyncHook
先看基类Hook
事件注册
有三种注册方式
触发
三种触发方式
在Hook的构造器中对这三个方法进行了赋值操作。
this._call,this._promise,this._callAsync,都是通过Object.defineProperties对Hook.prototype进行了重新的赋值。_这种方式很方便将_call方法赋值为另一个函数,而且可以直接在子类(如AsyncParallelHook)中,再利用Object.defineProperties将_call的vale赋值为null。就可以得到一个没有_call方法的子类。
promise和callAsync同理。
createCompileDelegate
平时调用call等方法就是调用lazyCompileHook,通过闭包保存了name,和type。
以call方法为例,调用call时,先执行this._createCall() 方法,把返回值赋值给 this[name] ,然后执行this[name]方法。
即
第二次及之后执行 call 方法会直接执行已经编译好的静态脚本,这里用到了惰性函数来优化代码的运行性能
看到这里也可以解释_insert方法里
this._resetCompilation()这个操作了,因为执行 call 方法时执行的是编译好的静态脚本,在添加监听函数是如果不重置,可能执行的静态脚本就不会包含当前注册的事件回调。惰性函数优点
this._createCall() 里面本质是调用了this.compiler 方法,但是基类Hook上的compiler() 方法是一个空实现。这个方法是子类实现的。
compile
compile 方法中去,它内部使用到了 factory 变量,这个变量是实例化 SyncHookCodeFactory 类的结果,而 SyncHookCodeFactory 类继承自 HookCodeFactory 类。
在compile中调用了factory的
setup和create方法,这两个方法都来自父类HookCodeFactoryinstance 接收的是 SyncHook 的实例,options 接收的是 Hook 类中
_createCall方法中传入的对象。setup方法职责就是把options对象中回调函数数组保存到_x属性上。_x 属性会在静态脚本内部会发挥重要作用。
new Function 第一个参数是函数的参数,第二个参数是函数体。
通过
this.args方法拼接参数,ths.args 方法接收一个对象,对象中有 before 和 after,before 主要用于拼接 context,after 主要用于拼接回调函数(例如 callAsync 传入的回调函数)。在生成函数体的时候调用了header方法和content方法。
header方法主要是申明一些变量,如context,_x,_taps等,在header方法中有一个this, 因为 new Function 生成的函数最终是赋值给 SyncHook 类的实例的 call 方法,所以是指向SyncHook 类的实例的。
content方法是在子类上定义的,它要是实现各个hooks的差异化代码。
根据不同的类型,content方法有不同的实现,目的是。让子类自己去写拼接的逻辑。
在所有的子类中,都实现了 content 方法,根据不同钩子执行流程的不同,调用了 callTapsSeries/callTapsParallel/callTapsLooping 并且会有 onError, onResult, onDone, rethrowIfPossible 这四中情况下的代码片段,callTapsSeries/callTapsParallel/callTapsLooping 都在基类的方法中,这三个方法中都会走到一个 callTap 的方法。
callTap
callTap 内是一次函数执行的模板,也是根据调用方式的不同,分为 sync/async/promise 三种。
callTapsSeries
this.callTap 中 onResult 和 onDone 的条件,就是说要么执行 onResult, 要么执行 onDone。
以SyncHook为例,
其生成的代码如下:
callTapsLooping
循环执行的钩子会执行callTapsLooping。
例子
生成代码如下:
callTapsParallel
AsyncParallelHook例子
总结一下 callTap 中实现了 sync/promise/async 三种基本的一次函数执行的模板,同时将涉及函数执行流程的代码 onError/onDone/onResult 部分抽离出来, 在 callTapsSeries/callTapsLooping/callTapsParallel 中,通过传入不同的 onError/onDone/onResult 实现出不同流程的模板 。
生成代码之后便是把 new Function 生成的函数返回给 call 方法,然后让 call 方法去执行静态脚本了。
其他hooks生成代码示例。
SyncBailHook
AsyncSeriesHook