Appearance
tapable 拦截器
钩子都提供额外的拦截器 API,在实例对象上使用 hook.intercept()
去添加一个钩子对象。
context : 如果定义了 那么 call、tap、loop 的第一个参数就是 _context, 且有一个事件流中如果定义了 context,那么这个 _context = {}否则为 _context = undefined
register : 在每一个事件流通过 tap、tapAsync、tapPromise 添加一个事件流的时候执行,参数为当前事件流处理后的 option 对象,可以修改此对象去自定义 option
call : 在每一个钩子函数执行的时候执行,参数为当前钩子函数的参数,如果定义了 context 第一个参数会变成 _context
tap : 在每一个事件流执行的时候执行,参数为当前当前的事件流对象(tap : {name , type,}),如果定义了 context 第一个参数会变成 _context
loop : Loop 类钩子函数独有,在每一个钩子函数循环执行的时候触发,参数为当前钩子函数的参数,如果定义了 context 第一个参数会变成 _context
register
在每一个事件流添加的时候执行,其参数为事件流的 option,需要 return 一个新的 option(可以在 register 中修改事件流的 option 对象)
源码
js
class Hook {
constructor(args) {}
_createCompileDelegate(name, type) {}
tap(options, fn) {
options = this._runRegisterInterceptors(options);
// 添加事件流
this._insert(options);
}
tapAsync(options, fn) {
options = this._runRegisterInterceptors(options);
this._insert(options);
}
tapPromise(options, fn) {
options = this._runRegisterInterceptors(options);
this._insert(options);
}
/**
* 回调每一个钩子函数中定义的register函数,参数为当前事件流的处理后的option 返回一个新的option
*/
_runRegisterInterceptors(options) {
for (const interceptor of this.interceptors) {
if (interceptor.register) {
// 就是执行register然后获得新的option 返回
const newOptions = interceptor.register(options);
if (newOptions !== undefined) options = newOptions;
}
}
return options;
}
}
可以看到 register 的作用就是在每一个事件流添加的时候可以让用户处理 option
call
我们看一下 call 的渲染
js
class HookCodeFactory {
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];
// 拦截器是否定义了 call
if (interceptor.call) {
// 处理拦截器的call,这个说拦截器的call是执行的时候才会执行
// 其入参也是
code += `${this.getInterceptor(i)}.call(${this.args({
before: interceptor.context ? '_context' : undefined,
})});\n`;
}
}
return code;
}
}
其是在每一个钩子函数执行生成伪代码的时候,在 this.header()中进行处理的,所以其不会因为多个事件流而执行多次
我们看他的渲染结果
js
'use strict';
var _context;
var _x = this._x;
var _taps = this.taps;
var _interceptors = this.interceptors;
// 处理call
_interceptors[0].call(_context, xxx, arg2);
_interceptors[1].call(xxx, arg2);
var _tap0 = _taps[0];
_interceptors[0].tap(_context, _tap0);
_interceptors[1].tap(_tap0);
var _fn0 = _x[0];
_fn0(xxx, arg2);
var _tap1 = _taps[1];
_interceptors[0].tap(_context, _tap1);
_interceptors[1].tap(_tap1);
var _fn1 = _x[1];
_fn1(xxx, arg2);
从上面可以看出 call 是在事件流执行之前执行,其参数是,如果定义了钩子函数 before 参数 context 那么就形参就是 _interceptors[0].call(context的值, h1.定义的时候传入的参数)
tap
在每一个事件流执行的时候先执行 tap 函数,传入当前事件流的 taps 对象({name, type , interceptors ,args})
源码
js
class HookCodeFactory {
callTap(tapIndex, { onError, onResult, onDone, rethrowIfPossible }) {
debugger;
let code = '';
let hasTapCached = false;
// 处理拦截器中 的 interceptor.tap
/*
var _context;
var _x = this._x;
var _taps = this.taps;
var _interceptors = this.interceptors;
_interceptors[0].call(xxx, arg2);
var _tap0 = _taps[0];
_interceptors[0].tap(_tap0);
var _fn0 = _x[0];
_fn0(xxx, arg2);
var _tap1 = _taps[1];
_interceptors[0].tap(_tap1);
var _fn1 = _x[1];
_fn1(xxx, arg2);
我们看上面生成的结果可以看出 tap 在生成可执行的字符串的时候是 在每一个事件流回调执行之前执行执行当前tap拦截,参数为每一个的事件流的 tap对象 其实就是 { name , fn ...}
var _tap0 = _taps[0];
_interceptors[0].tap(_tap0);
*/
for (let i = 0; i < this.options.interceptors.length; i++) {
const interceptor = this.options.interceptors[i];
if (interceptor.tap) {
// 原来所有的 taps是存放在 this._taps 中,下面需要多次访问那么为了提高性能就缓存一份 var _tap0 = _taps[0];
// 在上面的例子中可能不能看出问题,主要是当我们注册很多拦截器的时候
// 这时候就是
/*
var _tap0 = _taps[0];
_interceptors[0].tap(_tap0);
_interceptors[1].tap(_tap0);
_interceptors[2].tap(_tap0);
_interceptors[3].tap(_tap0);
var _fn0 = _x[0];
_fn0(xxx, arg2);
*/
if (!hasTapCached) {
code += `var _tap${tapIndex} = ${this.getTap(tapIndex)};\n`;
hasTapCached = true;
}
code += `${this.getInterceptor(i)}.tap(${interceptor.context ? '_context, ' : ''}_tap${tapIndex});\n`;
}
}
// 生成 var _fn0 = _x[0];
code += `var _fn${tapIndex} = ${this.getTapFn(tapIndex)};\n`;
const tap = this.options.taps[tapIndex];
// 下面生成 事件流的回调函数执行字符串
// 最简单的是 _fn0(xxx, arg2);
switch (tap.type) {
return code;
}
}
我们看他的渲染结果
js
'use strict';
var _context;
var _x = this._x;
var _taps = this.taps;
var _interceptors = this.interceptors;
// 处理call
_interceptors[0].call(_context, xxx, arg2);
_interceptors[1].call(xxx, arg2);
var _tap0 = _taps[0];
// 处理tap
_interceptors[0].tap(_context, _tap0);
_interceptors[1].tap(_tap0);
var _fn0 = _x[0];
_fn0(xxx, arg2);
var _tap1 = _taps[1];
_interceptors[0].tap(_context, _tap1);
_interceptors[1].tap(_tap1);
var _fn1 = _x[1];
_fn1(xxx, arg2);
loop
loop 是 syncLoopHook 独有的一个拦截器属性,其是每一次循环的时候执行一次(call 是每一次启动的时候调用一次,tap 是每一个事件流执行都执行一次)
js
'use strict';
var _context;
var _x = this._x;
var _taps = this.taps;
var _interceptors = this.interceptors;
_interceptors[0].call(_context, xxx, arg2);
_interceptors[1].call(xxx, arg2);
var _loop;
do {
_loop = false;
_interceptors[0].loop(_context, xxx, arg2);
var _tap0 = _taps[0];
_interceptors[0].tap(_context, _tap0);
_interceptors[1].tap(_tap0);
var _fn0 = _x[0];
var _result0 = _fn0(xxx, arg2);
if (_result0 !== undefined) {
_loop = true;
} else {
var _tap1 = _taps[1];
_interceptors[0].tap(_context, _tap1);
_interceptors[1].tap(_tap1);
var _fn1 = _x[1];
var _result1 = _fn1(xxx, arg2);
if (_result1 !== undefined) {
_loop = true;
} else {
if (!_loop) {
}
}
}
} while (_loop);