dehub
Version:
Data&Event MessageHub.
454 lines (453 loc) • 15.4 kB
JavaScript
"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;