没人说事件系统复杂啊。。我也只是用我的方式来枚举而已
export const Events = {
/**界面的打开 关闭事件 */
VIEW_OPEN: Symbol("VIEW_OPEN"),
VIEW_CLOSE: Symbol("VIEW_CLOSE"),
......
}
这不就唯一了
唯一不是关键,关键是发送事件的时候没有参数约束。
可以像c++那样 固定几个类型的函数样式
其实约束这东西,要看怎么去定义
肯定是2啦, 不过我一般都要求 用Enum 建立消息名字, 并且要求 在消息写明注释如
/** 这要是干啥的, 参数为 @param1 字符串,@param2 {propA:number,propB:number} */
你用的是3.x吗?2.x函数的tostring()方法返回的是空字符串;查了下官方说是为了节省代码去掉了。
所以我用了类似的方法处理了。
定义表,并转成Enum
===========const.ts文件
// 定义事件映射类型
const EventDefinitions = {
/**
* 国战 部队位置更新
* @param troopsSerial 部队序列
* @param x 位置x
* @param y 位置y
*/
TROOPS_POS_CHANGE: (troopsSerial: number, x: number, y: number)=>{},
MOVE_TROOPS_BY_KEY: (troopsKey: string)=>{},
ON_SAVE_MATRIX: ()=>{},
GET_CITY_TEAM_INFO: (cityId: number)=>{},
ADD_PATH: (path: Array<any>, key: string, movedLen: number, birthLen: number, speed: number)=>{},
REMOVE_PATH: (troopsKey: string)=>{},
UPDATE_PATH: (name: string, key: string)=>{},
} as const;
// 从 EventDefinitions 创建 EventEnum
export const EventEnum = Object.keys(EventDefinitions).reduce((acc, key) => {
acc[key as any] = key;
return acc;
}, {} as { [K in keyof typeof EventDefinitions]: K });
// 从 EventDefinitions 创建 EventMap 类型
export type EventMap = {
[K in keyof typeof EventDefinitions]: Parameters<typeof EventDefinitions[K]>
};
export type EventType = keyof EventMap;
定义带有类型的事件分发器,用Enum作为参数就可以传递事件了
import EventManager from "../../framework/core/event_mgr/EventManager";
import { EventMap, EventType } from "./Constants";
export class TypedEventManager {
public static on<K extends EventType>(
key: K,
callback: (...args: EventMap[K]) => void,
target?: any
) {
EventManager.on(key, callback, target);
}
public static once<K extends EventType>(
key: K,
callback: (...args: EventMap[K]) => void,
target?: any
) {
EventManager.once(key, callback, target);
}
public static off<K extends EventType>(
key: K,
callback?: (...args: EventMap[K]) => void,
target?: any
) {
EventManager.off(key, callback, target);
}
public static emit<K extends EventType>(
key: K,
...args: EventMap[K]
) {
EventManager.emit(key, ...args);
}
}
使用
有参数提醒,参数不对会报错。
总结
用这种方法,事件类型还是字符串,但是带有了参数约束。
我自己使用的是第二种
/**
* @description 触发指定事件,只触发匹配指定分类的事件处理函数
* @param eventName 触发的事件名称
* @param categories 触发的事件分类集合,可以是单个分类字符串或者分类字符串数组
* @param params 触发事件时传递的参数
* @returns void
*/
public emitCategories(eventName: string, categories: string | string[], …params: Array): void {
let eventsList = this._eventsCache.get(eventName) ?? [];
const categorySet = new Set(Array.isArray(categories) ? categories : [categories]);
if (!categorySet.has(EVENT_DEFAULT_CATEGORY)) {
// 如果不是发送默认消息,则筛选出符合条件的事件列表
eventsList = eventsList.filter((eventData) => categorySet.has(eventData.category));
}
for (const eventData of eventsList) {
if (eventData.target && (!eventData.once || eventData.valid)) {
eventData.func.call(eventData.target, ...params);
LogUtils.eventEmit(eventData.category, eventName, ...params);
if (eventData.once) {
eventData.valid = false;
this.off(eventData.id, EventRemoveType.Id);
}
}
}
}
/**
* @description 触发指定事件,触发所有匹配'default'分类的事件处理函数
* @param eventName 触发的事件名称
* @param params 触发事件时传递的参数
* @returns void
*/
public emit(eventName: string, ...params: Array<Object>): void {
this.emitCategories(eventName, EVENT_DEFAULT_CATEGORY, ...params);
}
我用的2.x
学到了