/** * BaseComponent - UI组件基类 * * 职责: * 1. 提供统一的组件生命周期管理 (init/show/hide/destroy) * 2. 自动管理EventBus事件监听的注册和注销 * 3. 自动管理组件内精灵的创建和销毁 * 4. 提供钩子方法供子类覆盖 * * 使用方式: * ```javascript * var MyComponent = Object.create(BaseComponent); * * MyComponent.init = function(config) { * this.layer = config.layer; * // 创建精灵 * var spriteId = SpriteManager.createSprite({...}); * this.addSprite(spriteId); * // 注册事件 * this.addEventListener('eventName', this.onEvent); * }; * * var instance = BaseComponent.create({ name: 'MyComponent', layer: 101 }); * instance.show(); * ``` * * 依赖: * - EventBus (必须先加载) * - SpriteManager (必须先加载) * * ============================================================================ * @guide 开发指南 - BaseComponent 两种继承模式 * ============================================================================ * * 【模式一:Object.create(推荐用于复杂视图,多图层组件)】 * * var GameView = Object.create(BaseComponent); * * GameView.init = function(config) { * // ⚠️ 必须手动重新声明所有实例属性(避免原型污染) * this.sprites = []; * this.eventListeners = {}; * this.isVisible = false; * this.isInitialized = false; * this.isDestroyed = false; * * this.name = config.name || 'GameView'; * this.layer = config.layer || 102; // BaseComponent.show() 需要此属性 * this.baseLayers = [102, 103, 104, 105]; // 多图层组件:show/hide 会操作所有图层 * * // 注册 EventBus 监听(使用 this.addEventListener 自动管理清理) * var self = this; * this.addEventListener(EventBus.Events.GAME_STARTED, function(data) { * self._onGameStarted(data); * }); * * // 注册精灵交互事件(通过 SpriteEventController) * SpriteEventController.registerMouseUp(sprites.CLOSE_BTN, function(event) { * self.hide(); * }); * * this.isInitialized = true; * }; * * // 在 UIBootstrap 中创建并使用(注意:直接调用 init,不用 create) * GameView.init({ name: 'GameView', layer: 102 }); * * 【模式二:BaseComponent.create(推荐用于简单单图层组件)】 * * // create 内部会调用 Object.create(BaseComponent) + 调用 init * var myComp = BaseComponent.create({ name: 'MyComp', layer: 101 }); * myComp.show(); * * 【何时用 Object.create vs BaseComponent.create】 * - 需要多图层(baseLayers) → Object.create * - 有大量自定义数据属性(this.data = {}) → Object.create * - 需要自定义 show/hide 逻辑 → Object.create * - 简单显隐控制,无额外逻辑 → BaseComponent.create * * 【生命周期钩子】 * GameView.onShow = function() { ... }; // show() 时调用 * GameView.onHide = function() { ... }; // hide() 时调用 * GameView.onDestroy = function() { ... }; // destroy() 时调用(清理timer等) * * 【destroy 行为说明】 * - 自动调用 EventBus.off 清理所有通过 addEventListener 注册的监听 * - 精灵只隐藏(hide),不删除(gameabc 不支持运行时删除静态精灵) * - 动态复制的精灵(SpriteCopyUtils)需在 onDestroy 中手动 remove * * 【模式三:IIFE 包裹 + 自定义 create 工厂(RoomView 模式)】 * * var RoomView = (function() { * 'use strict'; * var RoomView = Object.create(BaseComponent); * * RoomView.init = function(config) { * // 同样需要手动重声明实例属性 * this.sprites = []; * this.eventListeners = {}; * this.isVisible = false; * this.isInitialized = false; * this.isDestroyed = false; * this.name = config.name; * this.layer = config.layer; * }; * * // 自定义工厂方法(代替 BaseComponent.create,用于多步配置前创建实例) * RoomView.create = function(config) { * var instance = Object.create(RoomView); * config = config || {}; * config.layer = ROOM_VIEW.LAYER; // 固定图层,外部无需传 * // 显式调用 BaseComponent._init,内部会自动调用 instance.init(config) * BaseComponent._init.call(instance, config); * return instance; * }; * * return RoomView; * })(); * * // 使用:通过工厂创建实例 * var roomView = RoomView.create({ name: 'RoomView' }); * roomView.show(); * * 【super 方法调用(扩展父类方法时)】 * * RoomView.getState = function() { * // 调用 BaseComponent.getState 获取基础状态,再添加子类特有字段 * var state = BaseComponent.getState.call(this); * state.hasRoomData = !!this.roomData; * state.playerCount = this._getPlayerCount(); * return state; * }; * * 【show/hide 钩子(GameView 中使用的扩展钩子)】 * * // 除了 onShow/onHide/onDestroy,GameView 还使用了更细粒度的钩子: * GameView.onBeforeShow = function() { ... }; // show() 执行群组操作之前 * GameView.onAfterShow = function() { ... }; // show() 执行群组操作之后 * GameView.onBeforeHide = function() { ... }; // hide() 执行群组操作之前 * GameView.onAfterHide = function() { ... }; // hide() 执行群组操作之后 * // 这些钩子在 show()/hide() 内部通过 if (this.onAfterShow) this.onAfterShow() 调用 * * @version 1.0.0 * @date 2025-11-19 */ var BaseComponent = (function() { 'use strict'; // ==================== 组件基类定义 ==================== var BaseComponentProto = { /** * 组件名称 * @type {String} */ name: '', /** * 组件所在图层 * @type {Number} */ layer: 0, /** * 组件拥有的精灵ID列表 * @type {Array} */ sprites: null, /** * 组件注册的事件监听器 * 结构: { eventName: wrappedHandler } * @type {Object} */ eventListeners: null, /** * 组件是否可见 * @type {Boolean} */ isVisible: false, /** * 组件是否已初始化 * @type {Boolean} */ isInitialized: false, /** * 组件是否已销毁 * @type {Boolean} */ isDestroyed: false, /** * 组件配置 * @type {Object} */ config: null, // ==================== 内部方法 ==================== /** * 内部初始化方法 * @param {Object} config - 配置对象 * @param {String} config.name - 组件名称 * @param {Number} config.layer - 图层ID * @private */ _init: function(config) { if (this.isInitialized) { console.warn('⚠️ BaseComponent: 组件已初始化,跳过重复初始化:', this.name); return; } // 验证必需参数 if (!config || typeof config !== 'object') { console.error('❌ BaseComponent._init: config必须是对象'); return; } if (!config.name) { console.error('❌ BaseComponent._init: 缺少name参数'); return; } if (typeof config.layer !== 'number') { console.error('❌ BaseComponent._init: 缺少layer参数或类型错误'); return; } // 初始化属性 this.name = config.name; this.layer = config.layer; this.config = config; this.sprites = []; this.eventListeners = {}; this.isVisible = false; this.isDestroyed = false; this.isInitialized = true; // // console.log('🎨 BaseComponent: 初始化组件 "' + this.name + '" (图层: ' + this.layer + ')'); // 调用子类的init方法 try { this.init(config); } catch (error) { console.error('❌ BaseComponent: 组件初始化时发生异常:', error); this.isInitialized = false; } }, /** * 移除所有事件监听器 * @private */ _removeAllEventListeners: function() { for (var eventName in this.eventListeners) { if (this.eventListeners.hasOwnProperty(eventName)) { this.removeEventListener(eventName); } } }, /** * 隐藏所有精灵 * 注意: 精灵由编辑器创建,组件销毁时只隐藏精灵,不删除精灵 * @private */ _hideAllSprites: function() { if (!this.sprites || this.sprites.length === 0) { return; } // console.log('🙈 BaseComponent: 隐藏组件 "' + this.name + '" 的所有精灵 (数量: ' + this.sprites.length + ')'); for (var i = 0; i < this.sprites.length; i++) { var spriteId = this.sprites[i]; SpriteManager.hide(spriteId); } this.sprites = []; }, // ==================== 生命周期方法 ==================== /** * 初始化方法 - 子类覆盖 * 在此方法中创建精灵、注册事件监听 * @param {Object} config - 配置对象 */ init: function(config) { // 子类覆盖此方法 // console.log('📝 BaseComponent.init: 请在子类中覆盖此方法'); }, /** * 显示组件 * @returns {Boolean} 是否成功 */ show: function() { if (this.isDestroyed) { console.error('❌ BaseComponent.show: 组件已销毁,无法显示:', this.name); return false; } if (!this.isInitialized) { console.error('❌ BaseComponent.show: 组件未初始化,无法显示:', this.name); return false; } if (this.isVisible) { // console.log('ℹ️ BaseComponent.show: 组件已可见,跳过:', this.name); return true; } // console.log('👁️ BaseComponent: 显示组件 "' + this.name + '" (图层: ' + this.layer + ')'); // 显示图层 SpriteManager.showGroup(this.layer); this.isVisible = true; // 调用钩子方法 try { this.onShow(); } catch (error) { console.error('❌ BaseComponent.show: onShow钩子执行时发生异常:', error); } return true; }, /** * 隐藏组件 * @returns {Boolean} 是否成功 */ hide: function() { if (this.isDestroyed) { console.error('❌ BaseComponent.hide: 组件已销毁:', this.name); return false; } if (!this.isVisible) { // console.log('ℹ️ BaseComponent.hide: 组件已隐藏,跳过:', this.name); return true; } // console.log('🙈 BaseComponent: 隐藏组件 "' + this.name + '" (图层: ' + this.layer + ')'); // 隐藏图层 SpriteManager.hideGroup(this.layer); this.isVisible = false; // 调用钩子方法 try { this.onHide(); } catch (error) { console.error('❌ BaseComponent.hide: onHide钩子执行时发生异常:', error); } return true; }, /** * 销毁组件 * @returns {Boolean} 是否成功 */ destroy: function() { if (this.isDestroyed) { console.warn('⚠️ BaseComponent.destroy: 组件已销毁,跳过:', this.name); return false; } // console.log('💥 BaseComponent: 销毁组件 "' + this.name + '"'); // 移除所有事件监听 this._removeAllEventListeners(); // 隐藏所有精灵(精灵由编辑器创建,不删除) this._hideAllSprites(); // 隐藏图层 if (this.isVisible) { SpriteManager.hideGroup(this.layer); } // 调用钩子方法 try { this.onDestroy(); } catch (error) { console.error('❌ BaseComponent.destroy: onDestroy钩子执行时发生异常:', error); } // 标记为已销毁 this.isDestroyed = true; this.isVisible = false; this.isInitialized = false; return true; }, // ==================== 事件监听管理 ==================== /** * 添加事件监听器 * @param {String} eventName - 事件名称 * @param {Function} handler - 事件处理函数 * @returns {Boolean} 是否成功 */ addEventListener: function(eventName, handler) { if (!eventName || typeof eventName !== 'string') { console.error('❌ BaseComponent.addEventListener: eventName必须是字符串'); return false; } if (typeof handler !== 'function') { console.error('❌ BaseComponent.addEventListener: handler必须是函数'); return false; } if (this.eventListeners[eventName]) { console.warn('⚠️ BaseComponent.addEventListener: 事件已注册,将覆盖:', eventName); this.removeEventListener(eventName); } // 包装处理函数,绑定this上下文 var self = this; var wrappedHandler = function(data) { if (self.isDestroyed) { console.warn('⚠️ BaseComponent: 组件已销毁,忽略事件:', eventName); return; } try { handler.call(self, data); } catch (error) { console.error('❌ BaseComponent: 事件处理函数执行异常:', eventName, error); } }; // 注册到EventBus EventBus.on(eventName, wrappedHandler); // 记录监听器 this.eventListeners[eventName] = wrappedHandler; // console.log('📡 BaseComponent: 组件 "' + this.name + '" 注册事件:', eventName); return true; }, /** * 移除事件监听器 * @param {String} eventName - 事件名称 * @returns {Boolean} 是否成功 */ removeEventListener: function(eventName) { if (!this.eventListeners[eventName]) { console.warn('⚠️ BaseComponent.removeEventListener: 事件未注册:', eventName); return false; } var handler = this.eventListeners[eventName]; EventBus.off(eventName, handler); delete this.eventListeners[eventName]; // console.log('📴 BaseComponent: 组件 "' + this.name + '" 移除事件:', eventName); return true; }, /** * 触发事件(发送到EventBus) * @param {String} eventName - 事件名称 * @param {*} data - 事件数据 */ emit: function(eventName, data) { if (!eventName || typeof eventName !== 'string') { console.error('❌ BaseComponent.emit: eventName必须是字符串'); return; } // console.log('📤 BaseComponent: 组件 "' + this.name + '" 触发事件:', eventName); EventBus.emit(eventName, data); }, // ==================== 精灵管理 ==================== /** * 添加精灵到组件管理列表 * @param {Number} spriteId - 精灵ID * @returns {Boolean} 是否成功 */ addSprite: function(spriteId) { if (typeof spriteId !== 'number') { console.error('❌ BaseComponent.addSprite: spriteId必须是数字'); return false; } if (!SpriteManager.exists(spriteId)) { console.error('❌ BaseComponent.addSprite: 精灵不存在:', spriteId); return false; } if (this.sprites.indexOf(spriteId) !== -1) { console.warn('⚠️ BaseComponent.addSprite: 精灵已在管理列表中:', spriteId); return false; } this.sprites.push(spriteId); // // console.log('➕ BaseComponent: 组件 "' + this.name + '" 添加精灵:', spriteId); return true; }, /** * 从组件管理列表移除精灵(不销毁) * @param {Number} spriteId - 精灵ID * @returns {Boolean} 是否成功 */ removeSprite: function(spriteId) { var index = this.sprites.indexOf(spriteId); if (index === -1) { console.warn('⚠️ BaseComponent.removeSprite: 精灵不在管理列表中:', spriteId); return false; } this.sprites.splice(index, 1); // console.log('➖ BaseComponent: 组件 "' + this.name + '" 移除精灵:', spriteId); return true; }, /** * 获取组件管理的所有精灵ID * @returns {Array} 精灵ID数组 */ getSprites: function() { return this.sprites.slice(); // 返回副本 }, /** * 获取组件管理的精灵数量 * @returns {Number} 精灵数量 */ getSpriteCount: function() { return this.sprites.length; }, // ==================== 钩子方法 ==================== /** * 显示时的钩子方法 - 子类可覆盖 */ onShow: function() { // 子类可以覆盖此方法 }, /** * 隐藏时的钩子方法 - 子类可覆盖 */ onHide: function() { // 子类可以覆盖此方法 }, /** * 销毁时的钩子方法 - 子类可覆盖 */ onDestroy: function() { // 子类可以覆盖此方法 }, // ==================== 工具方法 ==================== /** * 获取组件状态信息 * @returns {Object} 状态对象 */ getState: function() { return { name: this.name, layer: this.layer, isVisible: this.isVisible, isInitialized: this.isInitialized, isDestroyed: this.isDestroyed, spriteCount: this.sprites.length, eventCount: Object.keys(this.eventListeners).length }; }, /** * 打印组件状态(调试用) */ printState: function() { var state = this.getState(); // console.log('📊 组件状态:', JSON.stringify(state, null, 2)); } }; // ==================== 工厂方法 ==================== /** * 创建组件实例 * @param {Object} config - 配置对象 * @param {String} config.name - 组件名称 * @param {Number} config.layer - 图层ID * @returns {Object} 组件实例 */ BaseComponentProto.create = function(config) { if (!config || typeof config !== 'object') { console.error('❌ BaseComponent.create: config必须是对象'); return null; } // 创建实例 var instance = Object.create(this); // 初始化实例 instance._init(config); if (!instance.isInitialized) { console.error('❌ BaseComponent.create: 组件初始化失败'); return null; } return instance; }; // 返回基类原型 return BaseComponentProto; })(); // 导出到全局(兼容ES5) if (typeof module !== 'undefined' && module.exports) { module.exports = BaseComponent; }