UNPKG

dehub

Version:
417 lines (326 loc) 11.9 kB
# 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()` 在组件销毁时移除事件监听