dehub
Version:
Data&Event MessageHub.
449 lines (448 loc) • 14.4 kB
JavaScript
;
/**
* DEHub 模块
* 这是一个事件中心模块,负责管理组件注册、事件分发和组件间通信
* @module DEHub
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.stop = exports.when = exports.error = exports.emit = exports.delComponent = exports.regComponent = exports.getAllComponents = exports.getComponent = exports.clearComponentSession = exports.EventContext = exports.DEHubConfig = void 0;
const lodash_1 = require("lodash");
const DETypes_1 = require("./DETypes");
// 全局组件映射表
const compMap = new Map();
// 全局事件处理函数映射表
const funcMap = new Map();
// 标签及其子标签缓存映射表
const tagSelfChildMap = new Map();
let regCmpTimeId;
let cmpInited = false;
// 默认配置选项
let _options = {
FuncExeTimeout: 3000,
AppLoadEventDelay: 517,
silent: false
};
/**
* 内部日志辅助
*/
const logWarn = (message, ...args) => {
if (!_options.silent)
console.warn(`[DEHub] ${message}`, ...args);
};
const logError = (message, ...args) => {
if (!_options.silent)
console.error(`[DEHub] ${message}`, ...args);
};
/**
* 配置DEHub选项
* @param options - 配置选项
*/
const DEHubConfig = (options) => {
_options = { ..._options, ...options };
};
exports.DEHubConfig = DEHubConfig;
/**
* Event Context
* 事件上下文
*/
class EventContext {
#emitContext;
#cancel;
#output;
constructor(emitContext, output) {
this.#emitContext = emitContext;
this.#cancel = false;
this.#output = output;
}
get sender() {
return this.#emitContext.sender;
}
get event() {
return this.#emitContext.event;
}
get stage() {
return this.#emitContext.stage;
}
get detail() {
return this.#emitContext.detail;
}
get status() {
return this.#emitContext.status;
}
get attribute() {
return this.#emitContext.attribute;
}
get property() {
return this.#emitContext.property;
}
get database() {
return this.#emitContext.database;
}
/**
* Cancel the event
* 取消事件
*/
get cancel() {
return this.#cancel;
}
set cancel(value) {
this.#cancel = value;
}
/**
* Output of the event
* 事件的输出
*/
get output() {
return this.#output;
}
/**
* Components of the event
* 事件的组件列表
*/
get components() {
return compMap;
}
}
exports.EventContext = EventContext;
/**
* 清除组件会话存储
*/
const clearComponentSession = () => {
try {
sessionStorage.clear();
}
catch (e) {
logError('Failed to clear sessionStorage', e);
}
};
exports.clearComponentSession = clearComponentSession;
/**
* 根据标签获取组件
* @param tag - 组件标签,可以是Tag对象、ObjTag对象或字符串
* @returns 返回匹配的组件或undefined
*/
const getComponent = (tag) => {
if (typeof tag === 'string') {
return compMap.get(tag);
}
let targetCmp;
if (tag instanceof DETypes_1.Tag) {
targetCmp = compMap.get(tag.path);
}
else {
targetCmp = compMap.get(new DETypes_1.Tag(tag).path);
}
return targetCmp;
};
exports.getComponent = getComponent;
/**
* 获取所有已注册组件
* @returns 组件映射表副本
*/
const getAllComponents = () => {
return new Map(compMap);
};
exports.getAllComponents = getAllComponents;
/**
* 处理组件注册后的操作
* @param comp - 要注册的组件
* @param preContext - 预处理上下文
*/
const postRegComponent = (comp, preContext) => {
const postContext = { ...preContext, stage: DETypes_1.EventStage.PostOperation };
comp.waitReady().then(() => {
const [postResponse, allPostFuncsResult] = (0, exports.emit)(postContext);
if (postResponse.cancel) {
return;
}
Promise.allSettled(allPostFuncsResult).then((results) => {
(0, DETypes_1.rejectedCheck)(results);
}).catch(e => {
(0, exports.error)(comp, e);
});
}).catch((reason) => {
(0, exports.error)(comp, reason);
});
};
/**
* 注册组件
* @param comp - 要注册的组件
* @returns 返回注册的组件
*/
const regComponent = (comp) => {
let preContext = {
sender: comp,
event: DETypes_1.EventNames.Registration,
stage: DETypes_1.EventStage.PreOperation,
detail: comp
};
const [preResponse, allPreFuncsResult] = (0, exports.emit)(preContext);
if (preResponse.cancel) {
return comp;
}
compMap.set(comp.tag.path, comp);
if (!allPreFuncsResult || allPreFuncsResult.length === 0) {
postRegComponent(comp, preContext);
}
else {
Promise.allSettled(allPreFuncsResult).then((results) => {
(0, DETypes_1.rejectedCheck)(results);
postRegComponent(comp, preContext);
}).catch((reason) => {
(0, exports.error)(comp, reason);
});
}
return comp;
};
exports.regComponent = regComponent;
/**
* 删除组件
* @param comp - 要删除的组件
* @returns Promise<void>
*/
const delComponent = async (comp) => {
const path = comp.tag.path;
const target = compMap.get(path);
if (!target) {
return;
}
const preContext = {
sender: target,
event: DETypes_1.EventNames.WillUnmount,
stage: DETypes_1.EventStage.PreOperation,
detail: target
};
try {
const [preResponse, prePromises] = (0, exports.emit)(preContext);
if (preResponse.cancel) {
return;
}
if (prePromises.length > 0) {
const results = await Promise.allSettled(prePromises);
(0, DETypes_1.rejectedCheck)(results);
}
compMap.delete(path);
const postContext = { ...preContext, stage: DETypes_1.EventStage.PostOperation };
const [postResponse, postPromises] = (0, exports.emit)(postContext);
if (postResponse.cancel) {
return;
}
if (postPromises.length > 0) {
const results = await Promise.allSettled(postPromises);
(0, DETypes_1.rejectedCheck)(results);
}
}
catch (err) {
(0, exports.error)(comp, err);
throw err;
}
};
exports.delComponent = delComponent;
/**
* 构建事件标签
* @param context - 事件上下文
* @param proxyTag - 代理标签
* @returns 返回构建的Tag对象
*/
const buildEventTags = (context, proxyTag = {}) => {
let eventTags = {
...(context.sender instanceof DETypes_1.Tag ? context.sender.members : context.sender?.tag?.members || {}),
event: context.event,
stage: context.stage
};
if (context.attribute)
eventTags.attribute = context.attribute;
if (context.property)
eventTags.property = context.property;
if (context.status)
eventTags.status = context.status;
if (context.database)
eventTags.database = context.database;
if (Object.keys(proxyTag).length > 0) {
Object.assign(eventTags, proxyTag);
}
return new DETypes_1.Tag(eventTags);
};
/**
* 触发事件
* @param context - 事件上下文
* @param proxyTag - 代理标签
* @returns [事件上下文, Promise数组, 事件ID]
*/
const emit = (context, proxyTag = {}) => {
const emitId = (0, lodash_1.uniqueId)('e');
const eventTags = buildEventTags(context, proxyTag);
let allEventPaths = tagSelfChildMap.get(eventTags.path);
if (!allEventPaths) {
allEventPaths = DETypes_1.TagsSerializer.selfAndChildTags(eventTags);
tagSelfChildMap.set(eventTags.path, allEventPaths);
}
const output = {};
const eventContext = new EventContext(context, output);
const asyncFuncList = [];
// 使用 Map 存储 promise -> handler 的映射,O(1) 查询
const promiseToHandler = new Map();
const handlerStatus = new Map();
foreachEvents: for (const ePath of allEventPaths) {
const handlers = funcMap.get(ePath) ?? [];
if (handlers.length === 0) {
continue;
}
for (const handler of handlers) {
try {
const result = handler.function(eventContext);
if (result instanceof Promise) {
asyncFuncList.push(result);
promiseToHandler.set(result, handler);
handlerStatus.set(handler, 'pending');
result.then(() => {
handlerStatus.set(handler, 'fulfilled');
}).catch(() => {
handlerStatus.set(handler, 'rejected');
});
setTimeout(() => {
if (handlerStatus.get(handler) === 'pending') {
logWarn(`[${emitId}] 异步事件(${ePath})函数 ${handler.alias ?? handler.name} 执行超时(>${_options.FuncExeTimeout}ms)`, { handler, context: eventContext });
}
}, _options.FuncExeTimeout);
}
if (eventContext.cancel === true) {
break foreachEvents;
}
}
catch (err) {
if (eventTags.members.event !== DETypes_1.EventNames.Exception) {
(0, exports.error)(context.sender, err);
}
throw err;
}
}
}
return [eventContext, asyncFuncList, emitId];
};
exports.emit = emit;
/**
* 错误处理(同步版本,内部自消化异步异常)
* @param sender - 错误发送者
* @param exception - 错误对象
*/
const error = (sender, exception) => {
const errorTags = {
sender,
event: DETypes_1.EventNames.Exception,
stage: DETypes_1.EventStage.PostOperation,
detail: typeof exception === 'string' ? new Error(exception) : exception
};
try {
const [output, errPromises] = (0, exports.emit)(errorTags);
if (errPromises.length > 0) {
Promise.allSettled(errPromises).catch((err) => {
logError('Error in error handler async promises:', err);
});
}
return [output, errPromises];
}
catch (err) {
logError('Error in error handler:', err);
return [null, []];
}
};
exports.error = error;
/**
* 注册事件监听
* @param tagFilter - 标签过滤器
* @param callback - 回调函数
* @param alias - 函数别名
*/
const when = (tagFilter, callback, alias) => {
if (!tagFilter.event && tagFilter.status === DETypes_1.ObjectStatus.Loading) {
tagFilter.event = DETypes_1.EventNames.Submit;
}
const filterTag = new DETypes_1.Tag(tagFilter);
let tags = tagSelfChildMap.get(filterTag.path);
if (!tags) {
tags = DETypes_1.TagsSerializer.selfAndChildTags(filterTag);
tagSelfChildMap.set(filterTag.path, tags);
}
let targetFuns = funcMap.get(filterTag.path) ?? [];
const funcItem = {
function: callback,
name: callback.name || 'anonymous',
alias: alias ?? (callback.name || 'anonymous')
};
// 使用引用比较替代深比较,性能提升且语义明确
if (targetFuns.find(t => t.function === funcItem.function)) {
return;
}
targetFuns.push(funcItem);
funcMap.set(filterTag.path, targetFuns);
};
exports.when = when;
/**
* 移除事件监听
* @param tagFilter - 标签过滤器
* @param callback - 要移除的回调函数
*/
const stop = (tagFilter, callback) => {
if (!tagFilter.event && tagFilter.status === DETypes_1.ObjectStatus.Loading) {
tagFilter.event = DETypes_1.EventNames.Submit;
}
const tag = new DETypes_1.Tag(tagFilter);
let targetFuns = funcMap.get(tag.path) ?? [];
const targetIndex = targetFuns.findIndex(t => t.function === callback);
if (targetIndex >= 0) {
targetFuns.splice(targetIndex, 1);
}
if (targetFuns.length === 0) {
funcMap.delete(tag.path);
tagSelfChildMap.delete(tag.path);
}
else {
funcMap.set(tag.path, targetFuns);
}
};
exports.stop = stop;
/**
* 应用组件加载完成处理函数
* @param context - 事件上下文
*/
const appCmpLoadedHandler = (context) => {
const comp = context.sender;
if (cmpInited === false) {
if (regCmpTimeId)
clearTimeout(regCmpTimeId);
regCmpTimeId = setTimeout(async () => {
cmpInited = true;
let appLoadedContext = {
sender: comp,
event: DETypes_1.EventNames.ComponentsLoaded,
stage: DETypes_1.EventStage.PostOperation,
detail: comp
};
try {
const [appResponse, allPostFuncsResult] = (0, exports.emit)(appLoadedContext);
if (appResponse.cancel) {
cmpInited = false;
return;
}
if (allPostFuncsResult && allPostFuncsResult.length > 0) {
(0, DETypes_1.rejectedCheck)(await Promise.allSettled(allPostFuncsResult));
}
// 注销事件侦听,确保只触发一次
(0, exports.stop)({
event: DETypes_1.EventNames.Registration,
stage: DETypes_1.EventStage.PreOperation
}, appCmpLoadedHandler);
}
catch (err) {
cmpInited = false;
(0, exports.error)(comp, err);
}
}, _options.AppLoadEventDelay);
}
};
// 注册应用组件加载完成事件监听
(0, exports.when)({
event: DETypes_1.EventNames.Registration,
stage: DETypes_1.EventStage.PreOperation
}, appCmpLoadedHandler);