事件分发器你们怎么设计

没人说事件系统复杂啊。。我也只是用我的方式来枚举而已

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 :joy:
学到了 :+1: :+1: :+1: