UNPKG

dehub

Version:
449 lines (448 loc) 14.4 kB
"use strict"; /** * 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);