--- description: "强类型数据绑定系统开发规范" globs: ["src/data/**/*.ts", "**/*Data*.ts"] alwaysApply: false type: "data-architecture" --- # 强类型数据绑定系统规范 ## 数据基类设计 ### DataBase 基类模式 ```typescript export class DataBase { /** 数据变化监听器 */ private _watchers: Map = new Map(); /** * 注册属性监听器 * @param path 属性路径 * @param callback 回调函数 */ public watch(path: string, callback: Function): void { if (!this._watchers.has(path)) { this._watchers.set(path, []); } this._watchers.get(path)!.push(callback); } /** * 触发属性变化通知 * @param path 属性路径 * @param value 新值 */ protected notify(path: string, value: any): void { if (this._watchers.has(path)) { this._watchers.get(path)!.forEach(callback => { callback(value); }); } } } ``` ### 数据类定义规范 ```typescript class GameData extends DataBase { private _level: number = 1; private _coins: number = 0; private _items: Item[] = []; // 使用 getter/setter 实现响应式 get level(): number { return this._level; } set level(value: number) { if (this._level !== value) { this._level = value; this.notify('level', value); } } get coins(): number { return this._coins; } set coins(value: number) { if (this._coins !== value) { this._coins = value; this.notify('coins', value); } } // 复杂属性的变化通知 addItem(item: Item): void { this._items.push(item); this.notify('items', this._items); this.notify('items.length', this._items.length); } } ``` ## 装饰器绑定系统 ### 强类型属性绑定 ```typescript export namespace data { /** * 强类型属性绑定装饰器 * @param dataClass 数据类构造函数 * @param selector 类型安全的路径选择器 * @param callback 变化回调函数 * @param immediate 是否立即触发 */ export function bindProp( dataClass: new () => T, selector: (data: T) => any, callback: (item: any, value?: any, data?: T) => void, immediate: boolean = false ) { return function (target: any, prop: string | symbol) { const path = `${dataClass.name}:${extractPathFromSelector(selector)}`; let ctor = target.constructor; ctor[BIND_METADATA_KEY] = ctor[BIND_METADATA_KEY] || []; ctor[BIND_METADATA_KEY].push({ prop, callback, path, immediate, isMethod: false }); }; } } ``` ### 方法绑定装饰器 ```typescript /** * 强类型方法绑定装饰器 * @param dataClass 数据类构造函数 * @param selector 类型安全的路径选择器 * @param immediate 是否立即触发 */ export function bindMethod( dataClass: new () => T, selector: (data: T) => any, immediate: boolean = false ) { return function (target: any, method: string | symbol, descriptor?: PropertyDescriptor) { const path = `${dataClass.name}:${extractPathFromSelector(selector)}`; let ctor = target.constructor; ctor[BIND_METADATA_KEY] = ctor[BIND_METADATA_KEY] || []; ctor[BIND_METADATA_KEY].push({ prop: method, callback: descriptor!.value, path, immediate, isMethod: true }); return descriptor; }; } ``` ## 使用示例 ### UI 数据绑定示例 ```typescript class PlayerData extends DataBase { name: string = ""; level: number = 1; exp: number = 0; maxExp: number = 100; // 计算属性 get expProgress(): number { return this.exp / this.maxExp; } } @uiclass("main", "player", "PlayerPanel") export class PlayerPanel extends Window { @uiprop nameLabel: GLabel; @uiprop levelLabel: GLabel; @uiprop expBar: GProgressBar; // 绑定玩家名称到标签 @data.bindProp(PlayerData, data => data.name, function(item, value) { this.nameLabel.text = value; }) private _nameBinding: any; // 绑定等级显示 @data.bindMethod(PlayerData, data => data.level) private onLevelChanged(value: number): void { this.levelLabel.text = `Lv.${value}`; } // 绑定经验条 @data.bindMethod(PlayerData, data => data.expProgress) private onExpChanged(progress: number): void { this.expBar.value = progress * 100; } protected onInit(): void { // 初始化绑定 data.initializeBindings(this); } protected onClose(): void { // 清理绑定 data.cleanupBindings(this); } } ``` ## 绑定管理器 ### BindManager 设计 ```typescript export class BindManager { private static _bindings: Map = new Map(); /** * 添加绑定信息 */ public static addBinding(bindInfo: BindInfo): void { const key = bindInfo.path; if (!this._bindings.has(key)) { this._bindings.set(key, []); } this._bindings.get(key)!.push(bindInfo); // 如果需要立即触发 if (bindInfo.immediate) { this.triggerBinding(bindInfo); } } /** * 清理目标对象的绑定 */ public static cleanup(target: any): void { this._bindings.forEach((bindings, path) => { const newBindings = bindings.filter(binding => binding.target !== target); if (newBindings.length === 0) { this._bindings.delete(path); } else { this._bindings.set(path, newBindings); } }); } /** * 触发路径对应的所有绑定 */ public static notifyChange(path: string, value: any, data: any): void { const bindings = this._bindings.get(path); if (bindings) { bindings.forEach(binding => { try { if (binding.isMethod) { binding.callback.call(binding.target, value, data); } else { binding.callback.call(binding.target, binding.target[binding.prop], value, data); } } catch (error) { console.error(`绑定回调执行错误: ${path}`, error); } }); } } } ``` ## 路径解析器 ### 路径提取函数 ```typescript /** * 从选择器函数中提取路径字符串 * 支持编译期类型检查的运行时路径解析 */ function extractPathFromSelector(selector: Function): string { const fnString = selector.toString(); // 匹配箭头函数: data => data.property.path let match = fnString.match(/\w+\s*=>\s*\w+\.(.+)/); if (!match) { // 匹配普通函数: function(data) { return data.property.path; } match = fnString.match(/return\s+\w+\.(.+);?\s*}/); } if (!match) { throw new Error('无效的路径选择器函数,请使用 data => data.property.path 格式'); } return match[1].trim(); } ``` ## 批量更新优化 ### BatchUpdater 设计 ```typescript export class BatchUpdater { private static _pendingUpdates: Set = new Set(); private static _updateTimer: number = 0; /** * 批量更新通知 * @param path 变化路径 * @param value 新值 * @param data 数据对象 */ public static scheduleUpdate(path: string, value: any, data: any): void { this._pendingUpdates.add(path); if (this._updateTimer === 0) { this._updateTimer = requestAnimationFrame(() => { this.flushUpdates(); }); } } /** * 执行批量更新 */ private static flushUpdates(): void { this._pendingUpdates.forEach(path => { // 获取最新值并触发更新 const [dataClassName, propertyPath] = path.split(':'); const dataInstance = DataRegistry.getInstance(dataClassName); if (dataInstance) { const value = this.getValueByPath(dataInstance, propertyPath); BindManager.notifyChange(path, value, dataInstance); } }); this._pendingUpdates.clear(); this._updateTimer = 0; } } ``` ## 数据绑定最佳实践 ### 1. 类型安全 - 使用泛型约束确保绑定的类型安全 - 路径选择器函数提供编译期检查 - 避免字符串路径,优先使用选择器函数 ### 2. 性能优化 - 使用批量更新减少频繁触发 - 及时清理不需要的绑定 - 避免在绑定回调中进行重度计算 ### 3. 内存管理 - 在组件销毁时调用 `data.cleanupBindings()` - 避免循环引用导致的内存泄漏 - 使用弱引用管理临时绑定 ### 4. 调试支持 - 提供绑定信息的调试接口 - 记录绑定的创建和销毁日志 - 支持绑定状态的实时监控