官方模拟版
2024年04月06日
一、SyncHook
1.1 SyncHook.js
import Hook from './Hook.js';
import HookCodeFactory from './HookCodeFactory.js';
class SyncHookCodeFactory extends HookCodeFactory {
content({ onError, onDone, rethrowIfPossible }) {
return this.callTapsSeries({
onError: (i, err) => onError(err),
onDone,
rethrowIfPossible
});
}
}
const factory = new SyncHookCodeFactory();
function COMPILE(options) {
factory.setup(this, options);
return factory.create(options);
}
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');
};
class SyncHook extends Hook {
constructor(args = [], name = undefined) {
super(args, name);
this.tapAsync = TAP_ASYNC;
this.tapPromise = TAP_PROMISE;
this.compile = COMPILE;
}
}
export default SyncHook;
1.2 Hook.js
/**
* @description: syncHook.call 触发事件 => this.call => this._createCall() => compile() 编译生成最终生成的执行函数
* @param {array} args
* 事件发布订阅思想
* syncHook.tap: 注册事件 => 本质上 this._taps.push({ type: 'sync', name: "", fn: fn});
* syncHoo.call: 触发已注册的事件 => 本质上通过 this._createCall() => compile() 编译生成最终生成的执行函数, 然后执行
*/
const CALL_DELEGATE = function (...args) {
this.call = this._createCall('sync');
return this.call(...args);
};
class Hook {
constructor(args = [], name = undefined) {
this._args = args;
this.name = name;
this.taps = [];
this.interceptors = [];
this._call = CALL_DELEGATE;
this.call = CALL_DELEGATE;
this._x = undefined;
this.compile = this.compile;
this.tap = this.tap;
}
tap(options, fn) {
this._tap('sync', options, fn);
}
/**
* @description: syncHook.tap 注册事件 => this._tap => this.taps.push({ type: 'sync', name: "", fn: fn});
* @param {*} type
* @param {*} options
* @param {*} fn
*/
_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.name !== 'string' || options.name === '') {
throw new Error('Missing name for tap');
}
options = Object.assign({ type, fn }, options);
this._insert(options);
}
_insert(item) {
// 当我们通过 hooks.tap 注册方法时每次都会触发 _insert 方法,故而我们在 _insert 方法中每次都重置 this.call 方法为编译方法 CALL_DELEGATE 。
this._resetCompilation();
this.taps.push(item);
}
compile(options) {
throw new Error('Abstract: should be overridden');
}
_createCall(type){
return this.compile({
type: type,
taps: this.taps,
args: this._args,
interceptors: this.interceptors,
});
}
_resetCompilation(){
this.call = this._call;
}
}
export default Hook;
1.3 HookCodeFactory.js
class HookCodeFactory {
constructor(config) {
this.config = config;
this.options = undefined;
this._args = undefined;
}
init(options) {
this.options = options;
this._args = options.args.slice();
}
setup(instance, options) {
instance._x = options.taps.map(i => i.fn);
}
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(', ');
}
}
header() {
let code = '';
code += 'var _context;\n';
code += 'var _x = this._x;\n';
return code;
}
create(options) {
this.init(options);
let fn;
switch (this.options.type) {
case 'sync':
fn = new Function(
this.args(),
'"use strict";\n' +
this.header() +
this.contentWithInterceptors({
onDone: () => '',
resultReturns: true,
rethrowIfPossible: true,
onError: err => `throw ${err}; \n`,
onResult: result => `return ${result}; \n`
})
);
break;
default:
break;
}
this.deinit();
return fn;
}
deinit() {
this._args = undefined;
this.options = undefined;
}
contentWithInterceptors(options) {
if (this.options.interceptors.length > 0) {
} else {
return this.content(options);
}
}
/**
* @description: callTapsSeries 遍历所有注册的 taps 编译成为对应的最终需要执行的函数
* @param {*} onDone
*/
callTapsSeries({ onDone }) {
let code = '';
let current = onDone;
if (this.options.taps.length === 0) {
return onDone();
}
for (let i = this.options.taps.length - 1; i >= 0; i--) {
const done = current;
const content = this.callTap(i, {
onDone: done
});
current = () => content;
}
code += current();
return code;
}
/**
* @description: callTap 根据单个 tap 的类型生成对应的函数调用语句进行返回
* @param {*} tapIndex
* @param {*} onDone
*/
callTap(tapIndex, { onDone }) {
let code = '';
code += `var _fn${tapIndex} = ${this.getTapFn(tapIndex)};\n`;
const tap = this.options.taps[tapIndex];
switch (tap.type) {
case 'sync':
code += `_fn${tapIndex}(${this.args()});\n`;
break;
default:
break;
}
if (onDone) {
code += onDone();
}
return code;
}
/**
* @description: getTapFn 从this._x中获取函数内容 this._x[index]
* @param {*} idx
*/
getTapFn(idx) {
return `_x[${idx}]`;
}
}
export default HookCodeFactory;
1.4 测试
import SyncHook from "./SyncHook.js";
// 初始化 syncHook 钩子
const syncHook = new SyncHook(["arg1","arg2","arg3"]);
// 注册 syncHook 钩子
syncHook.tap("syncHook1", (arg1,arg2,arg3)=>{
console.log("syncHook1",arg1,arg2,arg3);
});
syncHook.tap("syncHook2", (arg1,arg2,arg3)=>{
console.log("syncHook2",arg1,arg2,arg3);
});
// 触发 syncHook 钩子
setTimeout(()=>{
syncHook.call(1,2,3);
},3000);