UNPKG

dehub

Version:
454 lines (453 loc) 15.4 kB
"use strict"; /** * DEComp 模块 * 这是一个组件管理模块,负责组件的生命周期、状态管理和事件处理 * @module DEComp */ Object.defineProperty(exports, "__esModule", { value: true }); const lodash_1 = require("lodash"); const DEHub_1 = require("./DEHub"); const DETypes_1 = require("./DETypes"); /** 全局组件映射表 */ const compMap = new Map(); /** 状态变更事件处理函数映射表 */ const onStateChangingMap = new Map(); /** 组件就绪等待器映射表 */ const onCompReadyWaitersMap = new Map(); /** 脏状态映射表 */ const dirtyStateMap = new Map(); /** 组件就绪状态映射表 */ const compReadMap = new Map(); /** * 组件类 * 实现了 IDEComp 接口,提供组件的状态管理和生命周期功能 */ class DEComp { /** 组件标签 */ #tag; /** 最后更新时间 */ #updateOn; /** * 构造函数 * @param tag 组件标签 * @param options 配置选项 * @param state 初始状态 */ constructor(tag, options, state) { if (tag instanceof DETypes_1.Tag) { this.#tag = tag; } else { this.#tag = new DETypes_1.Tag(tag); } if (!onStateChangingMap.has(this.tag.path)) { onStateChangingMap.set(this.tag.path, []); } if (!onCompReadyWaitersMap.has(this.tag.path)) { onCompReadyWaitersMap.set(this.tag.path, []); } if (!dirtyStateMap.has(this.tag.path)) { dirtyStateMap.set(this.tag.path, {}); } if (!compReadMap.has(this.tag.path)) { compReadMap.set(this.tag.path, false); } this.#updateOn = performance.now(); // 设置默认配置选项 if (options) { if (options.autoReady === undefined) { options.autoReady = true; } if (options.useSession === undefined) { options.useSession = false; } } let stateObj; // 从 sessionStorage 加载状态 if (options?.useSession === true) { const sessionState = this.#loadSessionState(); if (sessionState && sessionState.state) { stateObj = sessionState.state; } } stateObj = (0, lodash_1.merge)(stateObj ?? {}, state ?? {}); if (!compMap.has(this.tag.path)) { this.compStore = { options: options ?? { autoReady: true, useSession: false }, state: stateObj }; this.#mounting(); } } /** * 获取组件存储 */ get compStore() { return compMap.get(this.tag.path); } /** * 设置组件存储 */ set compStore(state) { compMap.set(this.tag.path, state); if (this.options.useSession) { sessionStorage.setItem(this.tag.path, JSON.stringify(this.compStore)); } } /** * 获取状态变更事件处理函数列表 */ get onStateChangingHandler() { return onStateChangingMap.get(this.tag.path); } /** * 获取就绪等待器列表 */ get onReadyWaiters() { return onCompReadyWaitersMap.get(this.tag.path); } /** * 获取脏状态 */ get dirtyState() { return dirtyStateMap.get(this.tag.path); } /** * 设置脏状态 */ set dirtyState(dirty) { dirtyStateMap.set(this.tag.path, dirty); } /** * 组件挂载 * 初始化组件并触发挂载事件 */ #mounting = async () => { let preContext = { sender: this, event: DETypes_1.EventNames.Mounting, stage: DETypes_1.EventStage.PreOperation, detail: { ...this.compStore, tag: this.tag } }; const [context, prePromise] = (0, DEHub_1.emit)(preContext); const prePromiseResults = await Promise.allSettled(prePromise); (0, DETypes_1.rejectedCheck)(prePromiseResults); if (context.cancel === true) { throw Error(`Component loading is cancelled`); } if (context.output.state) { this.compStore.state = (0, DETypes_1.mergeObject)(this.compStore.state, context.output.state); } let emitPostContext = { ...preContext, sender: this, stage: DETypes_1.EventStage.PostOperation }; (this.options.autoReady === undefined || this.options.autoReady === true) && this.setReady(true); const [postContext, postPromises] = (0, DEHub_1.emit)(emitPostContext); if (this.options.useSession) { sessionStorage.setItem(this.tag.path, JSON.stringify(this.compStore)); } if (postPromises && postPromises.length > 0) { await Promise.allSettled(postPromises); } }; /** * 从 sessionStorage 加载状态 */ #loadSessionState = () => { const stateImage = sessionStorage.getItem(this.tag.path); if (!stateImage) { return { state: {}, options: this.options }; } try { return JSON.parse(stateImage); } catch (e) { (0, DEHub_1.error)(this, Error(`${this.tag.path}组件从存储中还原状态失败。Image:${stateImage}`)); return { state: {}, options: this.options }; // 返回一个有效的对象 } }; /** * 获取配置选项 */ get options() { return this.compStore?.options ?? { autoReady: true, useSession: false }; } /** * 设置配置选项 */ set options(options) { if (!this.compStore) { this.compStore = { options: options ?? { autoReady: true, useSession: false }, state: {} }; } else { this.compStore.options = options; } } /** * 等待组件就绪 */ waitReady = () => { return new Promise((resolve, reject) => { if (this.isReady === true) { resolve(); return; } const promiseGroup = this.onReadyWaiters; promiseGroup?.push([resolve, reject]); }); }; /** * 解析就绪回调函数 */ #resolveReadyCallbacks = () => { const promiseGroup = this.onReadyWaiters; if (promiseGroup.length === 0) return; promiseGroup.forEach((promiseItem, pIndex) => { const resolve = promiseItem[0]; resolve(); }); onCompReadyWaitersMap.set(this.tag.path, []); }; /** * 设置组件就绪状态 * @param isReady 是否就绪 */ setReady = (isReady) => { return new Promise((resolve, reject) => { if (this.isReady === isReady) { if (isReady === true) { this.#resolveReadyCallbacks(); } return resolve(); } const preValue = this.isReady; this.isReady = isReady; if (isReady === true) { this.#resolveReadyCallbacks(); } const emitContext = { sender: this, event: DETypes_1.EventNames.StateChanging, stage: DETypes_1.EventStage.PostOperation, detail: { value: isReady, preImage: preValue, postImage: isReady }, property: '$isReady' }; try { const [outputContext, outputPromises] = (0, DEHub_1.emit)(emitContext); Promise.allSettled(outputPromises).then((results) => { (0, DETypes_1.rejectedCheck)(results); resolve(); }).catch((reason) => { reject(reason); }); } catch (err) { (0, DEHub_1.error)(this, err); reject(err); } }); }; /** * 获取组件就绪状态 */ get isReady() { return compReadMap.get(this.tag.path) ?? false; } /** * 设置组件就绪状态 */ set isReady(value) { compReadMap.set(this.tag.path, value); } /** * 获取组件标签 */ get tag() { return this.#tag; } /** * 获取组件状态 */ get state() { if (this.options.useSession === true && performance.now() - this.#updateOn > 3000) { const sessionState = this.#loadSessionState(); if (sessionState) { // 只更新state部分,保留其他属性 this.compStore = { ...this.compStore, state: sessionState.state || {} }; } } return (this.compStore.state ?? {}); } /** * 注册状态变更事件处理函数 * @param callback 回调函数 */ onStateChanging(callback) { if (this.onStateChangingHandler.indexOf(callback) >= 0) { return; } this.onStateChangingHandler.push(callback); } /** * 移除事件处理函数 * @param callback 要移除的函数 */ removeHandler(callback) { const index = this.onStateChangingHandler.indexOf(callback); if (index < 0) { return; } this.onStateChangingHandler.splice(index, 1); } /** * 触发状态变更事件 */ #emitOnStateChanged = (eventContext) => { if (this.onStateChangingHandler.length > 0) { for (const func of this.onStateChangingHandler) { try { func.call(this, eventContext); } catch (err) { (0, DEHub_1.error)(this, err); } } } }; /** 更新版本号 */ #updateVer = 0; /** 状态版本映射表 */ #stateVer = new Map(); /** * 更新组件状态 * @param target 目标状态 * @param deep 是否深度更新 */ update = (target, deep = false) => { const updateVer = this.#updateVer++; return new Promise(async (resolve, reject) => { const targetKeys = Object.keys(target); const updateTarget = {}; const imageState = {}; targetKeys.forEach(k => { imageState[k] = this.state[k]; if ((0, lodash_1.isEqual)(imageState[k], target[k])) { return; } updateTarget[k] = target[k]; this.#stateVer.set(k, updateVer); }); let dirtyState = { ...this.dirtyState }; let outResult = (0, DETypes_1.mergeObject)(imageState, updateTarget, deep); const tempState = (0, DETypes_1.mergeObject)(imageState, dirtyState, deep); if ((0, lodash_1.isEqual)(outResult, tempState)) { return resolve({}); } dirtyState = (0, lodash_1.merge)(dirtyState ?? {}, updateTarget); const preImage = { ...this.state }; this.dirtyState = dirtyState; const idPath = this.#tag.path; const updateKeys = Object.keys(updateTarget); let lastPreContext = null; for (const property of updateKeys) { const emitContext = { sender: this, event: DETypes_1.EventNames.StateChanging, stage: DETypes_1.EventStage.PreOperation, detail: { value: updateTarget[property], preImage: preImage[property], postImage: outResult[property] }, property }; try { const [outputContext, outputPromises] = (0, DEHub_1.emit)(emitContext); if (!lastPreContext) { lastPreContext = outputContext; } if (outputContext.cancel === true) { return reject([outputContext, outputPromises]); } if (outputPromises && outputPromises.length > 0) { const asyncResult = await Promise.allSettled(outputPromises); (0, DETypes_1.rejectedCheck)(asyncResult); } } catch (err) { (0, DEHub_1.error)(this, err); return reject(err); } } const stateValue = (0, DETypes_1.clearNullNodes)((0, DETypes_1.mergeObject)(this.compStore.state, outResult)); targetKeys.forEach(k => { if (updateVer === this.#stateVer.get(k)) { // 只有当当前更新是最新的更新时才删除dirtyState if (this.dirtyState[k] !== undefined) { delete this.dirtyState[k]; } } else { // 如果不是最新的更新,保留原来的状态 if (this.state[k] !== undefined) { stateValue[k] = this.state[k]; } } }); this.compStore.state = stateValue; if (this.options.useSession === true) { sessionStorage.setItem(idPath, JSON.stringify(this.compStore)); this.#updateOn = performance.now(); } /** 处理onStateChanging函数 */ if (this.onStateChangingHandler && this.onStateChangingHandler.length > 0 && lastPreContext) { this.#emitOnStateChanged({ ...lastPreContext, sender: this, detal: { value: updateTarget, preImage: preImage, postImage: outResult }, property: Object.keys(updateTarget) }); } /** POST */ for (const property of updateKeys) { const emitContext = { sender: this, event: DETypes_1.EventNames.StateChanging, stage: DETypes_1.EventStage.PostOperation, detail: { value: updateTarget[property], preImage: preImage[property], postImage: outResult[property] }, property }; try { const [postResult, postPromises] = (0, DEHub_1.emit)(emitContext); if (postPromises.length > 0) { const asyncReusults = await Promise.allSettled(postPromises); (0, DETypes_1.rejectedCheck)(asyncReusults); } } catch (err) { (0, DEHub_1.error)(this, err); reject(err); } } //const postDirty = {...this.#dirtyState}; return resolve(updateTarget); }); }; } exports.default = DEComp;