dehub
Version:
Data&Event MessageHub.
417 lines (326 loc) • 11.9 kB
Markdown
# DEHub
DEHub is a message-driven library for JavaScript-based clients. It unifies the handling of events and data in all clients by using messages for subscription and processing. This allows all components and business processes in a page to handle business logic in a subscribable manner, and enables access to all registered components through context.
## Key Features
- Component Registration and Management
- Supports batch component registration
- Supports parent-child component relationships
- Supports asynchronous component loading
- Supports component state management and updates
- Supports component lifecycle events
- **Supports resource cleanup via `destroy()`**
- Data Management
- Supports multiple data loading modes (localDB, custom, none)
- Supports data singleton maintenance
- Supports virtual fields
- Supports data reset and cleanup
- Supports data cache management
- Supports high-performance handling of large datasets
- **Supports safe IndexedDB operations with configurable timeouts**
- Event System
- Supports event subscription and processing
- Supports event pre-processing and post-processing
- Supports event context access
- **Supports `IEventContext` interface for type-safe event handling**
## Quick Start
### Component Registration
```typescript
// Create and register a basic component
const tag = new Tag({ id: 'cmp1' });
const comp1 = new DEComp<any>(tag);
regComponent(comp1);
// Wait for component to be ready
await comp1.waitReady();
// Update component state
await comp1.update({ a: 1, b: 'abc' });
// Clean up when done
await comp1.destroy();
```
### Component State Management
```typescript
// Listen for component state changes
when({
id: 'cmp1',
event: EventNames.StateChanging,
stage: EventStage.PostOperation
}, (context: IEventContext) => {
const comp = context.sender as DEComp;
// Handle state changes
});
// Update component state
await comp1.update({ a: 2 });
```
### Data Management
```typescript
// Create data tag
const IdTag = (entity: string, id: string): Tag => {
return new Tag({ entity, id });
};
// Get data object
const user1Tag = IdTag('user.demo@org0', 'user1');
const user1Data = await fetchData(user1Tag, {
loadingMode: 'custom',
default: { name: 'Default' }
});
// Wait for data to load
await user1Data.until(ObjectStatus.Ready);
// Modify data
await user1Data.set("name", "John");
await user1Data.set("age", 25);
// Submit data
await user1Data.submit();
// Clean up when done
await user1Data.destroy();
```
### Data Loading Modes
```typescript
// LocalDB mode - Load from local database
const localData = await fetchData({ id: 'localData1' }, {
loadingMode: 'localDB',
default: { a: 1 }
});
// Custom mode - Custom loading logic
const customData = await fetchData({ id: 'customData1' }, {
loadingMode: 'custom'
});
// None mode - No automatic loading
const noneData = await fetchData({ id: 'noneData1' }, {
loadingMode: 'none',
autoLoad: false
});
```
### Virtual Fields
```typescript
// Set virtual field
await data.set("virtualField", value, true);
// Virtual fields are not saved to database
expect(data.virtual.virtualField).toBeDefined();
expect(data.original.virtualField).toBeUndefined();
```
### Configuration
```typescript
// Configure DEHub options
DEHubConfig({
FuncExeTimeout: 5000, // Async handler timeout (ms), default: 3000
AppLoadEventDelay: 517, // App loaded event delay (ms), default: 517
silent: false // Suppress console warnings, default: false
});
```
### Resource Cleanup
```typescript
// Components
const comp = new DEComp(new Tag({ role: 'input', id: 'username' }));
// ... use component
await comp.destroy(); // Cleans up timers, sessionStorage, event handlers
// Data objects
const data = await fetchData(new Tag({ type: 'user', id: '1' }), {
loadingMode: 'localDB'
});
// ... use data
await data.destroy(); // Cleans up IndexedDB timers, waiters, state maps
```
## Performance Optimization
- Efficient handling of large datasets
- Data cache management
- Batch component registration and state updates
- Asynchronous loading and lazy initialization
- **O(1) async handler timeout tracking** (replaced O(n²) array scanning)
- **Reference-based handler deduplication** (replaced deep comparison)
- **Debounced sessionStorage writes** (300ms throttle to reduce sync I/O)
- **Tag key limit safeguard** (warns when tag keys exceed 10 to prevent 2^n explosion)
## Important Notes
1. Data Object State Management
- Modified data enters dirty state
- Data updates to original state after submission
- Virtual fields are not saved to database
- **IndexedDB read timeout is 5000ms and will NOT delete data on timeout**
2. Component Lifecycle
- Mounting event triggered on component registration
- StateChanging event triggered on state changes
- WillUnmount event triggered on component unload
- **Always call `destroy()` when a component or data object is no longer needed to prevent memory leaks**
3. Data Loading Modes
- localDB: Suitable for local data storage
- custom: Suitable for custom data loading logic
- none: Suitable for manual data loading control
4. Event Handling
- Use `IEventContext` instead of `EventContext` for typing event handlers
- `EventContext` is the runtime class; `IEventContext` is the interface
## Best Practices
1. Component Registration
- Use meaningful component IDs
- Properly utilize parent-child relationships
- **Always call `destroy()` to release resources when unmounting**
2. Data Management
- Choose appropriate loading modes
- Properly use virtual fields
- Pay attention to data state management
- **Call `destroy()` on data objects to clean up IndexedDB connections and waiters**
3. Event Handling
- Properly use event pre-processing and post-processing
- Consider performance impact of event handling
- Avoid circular dependencies in event handling
- Use `stop()` to remove event listeners when components are destroyed
---
# DEHub (中文)
DEHub 是一个基于 JavaScript 的客户端消息驱动库,通过消息订阅的方式统一处理客户端的事件与数据。它允许页面中的所有组件和业务流程以订阅的方式处理业务逻辑,并能够通过上下文访问所有已注册组件。
## 主要特性
- 组件注册与管理
- 支持组件的批量注册
- 支持组件的父子关系
- 支持组件的异步加载
- 支持组件的状态管理和更新
- 支持组件的生命周期事件
- **支持通过 `destroy()` 主动释放资源**
- 数据管理
- 支持多种数据加载模式(localDB, custom, none)
- 支持数据单例维护
- 支持虚拟字段(virtual fields)
- 支持数据重置和清理
- 支持数据缓存管理
- 支持大量数据的高性能处理
- **支持安全的 IndexedDB 操作与可配置超时**
- 事件系统
- 支持事件订阅和处理
- 支持事件预处理和后处理
- 支持事件上下文访问
- **支持 `IEventContext` 接口实现类型安全的事件处理**
## 快速开始
### 组件注册
```typescript
// 创建并注册一个基础组件
const tag = new Tag({ id: 'cmp1' });
const comp1 = new DEComp<any>(tag);
regComponent(comp1);
// 等待组件就绪
await comp1.waitReady();
// 更新组件状态
await comp1.update({ a: 1, b: 'abc' });
// 使用完毕后清理资源
await comp1.destroy();
```
### 组件状态管理
```typescript
// 监听组件状态变更
when({
id: 'cmp1',
event: EventNames.StateChanging,
stage: EventStage.PostOperation
}, (context: IEventContext) => {
const comp = context.sender as DEComp;
// 处理状态变更
});
// 更新组件状态
await comp1.update({ a: 2 });
```
### 数据管理
```typescript
// 创建数据标签
const IdTag = (entity: string, id: string): Tag => {
return new Tag({ entity, id });
};
// 获取数据对象
const user1Tag = IdTag('user.demo@org0', 'user1');
const user1Data = await fetchData(user1Tag, {
loadingMode: 'custom',
default: { name: 'Default' }
});
// 等待数据加载完成
await user1Data.until(ObjectStatus.Ready);
// 修改数据
await user1Data.set("name", "John");
await user1Data.set("age", 25);
// 提交数据
await user1Data.submit();
// 使用完毕后清理资源
await user1Data.destroy();
```
### 数据加载模式
```typescript
// LocalDB 模式 - 从本地数据库加载
const localData = await fetchData({ id: 'localData1' }, {
loadingMode: 'localDB',
default: { a: 1 }
});
// Custom 模式 - 自定义加载逻辑
const customData = await fetchData({ id: 'customData1' }, {
loadingMode: 'custom'
});
// None 模式 - 不自动加载
const noneData = await fetchData({ id: 'noneData1' }, {
loadingMode: 'none',
autoLoad: false
});
```
### 虚拟字段
```typescript
// 设置虚拟字段
await data.set("virtualField", value, true);
// 虚拟字段不会被保存到数据库
expect(data.virtual.virtualField).toBeDefined();
expect(data.original.virtualField).toBeUndefined();
```
### 全局配置
```typescript
// 配置 DEHub 选项
DEHubConfig({
FuncExeTimeout: 5000, // 异步函数执行超时(毫秒),默认 3000
AppLoadEventDelay: 517, // 应用加载完成事件延迟(毫秒),默认 517
silent: false // 是否静默控制台警告,默认 false
});
```
### 资源释放
```typescript
// 组件资源释放
const comp = new DEComp(new Tag({ role: 'input', id: 'username' }));
// ... 使用组件
await comp.destroy(); // 清理定时器、sessionStorage、事件监听
// 数据对象资源释放
const data = await fetchData(new Tag({ type: 'user', id: '1' }), {
loadingMode: 'localDB'
});
// ... 使用数据
await data.destroy(); // 清理 IndexedDB 定时器、等待器、状态映射
```
## 性能优化
- 支持大量数据对象的高效处理
- 支持数据缓存管理
- 支持组件的批量注册和状态更新
- 支持异步加载和延迟初始化
- **O(1) 异步事件超时检测**(替代原有的 O(n²) 数组扫描)
- **引用比较去重**(替代原有的深比较去重)
- **sessionStorage 防抖写入**(300ms 节流,减少同步 I/O 阻塞)
- **标签键数安全限制**(超过 10 个键时告警,防止 2^n 组合爆炸)
## 注意事项
1. 数据对象的状态管理
- 修改数据会进入 dirty 状态
- 提交后数据会更新到 original 状态
- 虚拟字段不会被保存到数据库
- **IndexedDB 读取超时为 5000ms,超时后不会自动删除数据**
2. 组件生命周期
- 组件注册时会触发 Mounting 事件
- 状态变更时会触发 StateChanging 事件
- 组件卸载时会触发 WillUnmount 事件
- **组件或数据对象不再使用时,请务必调用 `destroy()` 防止内存泄漏**
3. 数据加载模式
- localDB: 适合本地数据存储
- custom: 适合自定义数据加载逻辑
- none: 适合手动控制数据加载
4. 事件处理
- 事件处理函数类型请使用 `IEventContext` 而非 `EventContext`
- `EventContext` 是运行时类,`IEventContext` 是接口类型
- 组件卸载时请使用 `stop()` 移除事件监听
## 最佳实践
1. 组件注册
- 使用有意义的组件 ID
- 合理使用组件的父子关系
- **组件卸载时调用 `destroy()` 释放全局状态、定时器和存储**
2. 数据管理
- 选择合适的加载模式
- 合理使用虚拟字段
- 注意数据状态的管理
- **数据对象废弃时调用 `destroy()` 清理 IndexedDB 连接和等待器**
3. 事件处理
- 合理使用事件预处理和后处理
- 注意事件处理的性能影响
- 避免事件处理的循环依赖
- 使用 `stop()` 在组件销毁时移除事件监听