NestJS中如何使用@OnEvent装饰器监听多个指定事件
NestJS @OnEvent 多事件绑定问题解决方案
问题表现
在NestJS项目中基于@OnEvent装饰器开发事件监听器时,存在以下现象:
- 直接以数组形式传入
CONVERSATION_UPDATE、CONVERSATION_DELETE、CONVERSATION_CREATE多个事件名作为装饰器参数时,监听器无法正常触发,异常写法如下:
@OnEvent([CONVERSATION_UPDATE, CONVERSATION_DELETE, CONVERSATION_CREATE], { async: true, }) async handleOrderCreatedEvent(payload: ConversationChangeEvent) { await this.lessonService.getAssetById(payload); }
- 为每个事件单独配置
@OnEvent装饰器可以正常生效,但会产生代码冗余 - 使用
CONVERSATION_MODIFY = 'conversation.*'通配符的写法可以正常运行,但无法灵活绑定任意数量的指定事件,存在同前缀事件误触发的风险
问题原因
@nestjs/event-emitter提供的原生@OnEvent装饰器,第一个参数仅支持传入单个字符串格式的事件名,不支持直接传入事件名数组,传参格式不匹配是导致监听器不生效的核心原因。
可行实现方案
方案1:同方法堆叠多个装饰器(原生无封装)
同一个事件处理方法上可以直接堆叠多个@OnEvent装饰器,不需要重复编写处理逻辑,没有额外封装成本:
@OnEvent(CONVERSATION_UPDATE, { async: true }) @OnEvent(CONVERSATION_DELETE, { async: true }) @OnEvent(CONVERSATION_CREATE, { async: true }) async handleOrderCreatedEvent(payload: ConversationChangeEvent) { await this.lessonService.getAssetById(payload); }
- 适用场景:绑定事件数量少(2-3个),不想额外增加封装代码的场景,行为完全和原生逻辑一致,稳定性最高。
方案2:自定义多事件绑定装饰器(灵活度最高)
封装支持数组传参的自定义装饰器,内部自动为每个事件注册原生监听器,一次封装后全项目可复用,支持绑定任意数量的任意事件名(包括通配符格式事件):
- 先编写自定义装饰器代码,可存放在项目公共装饰器目录:
import { OnEvent } from '@nestjs/event-emitter'; /** * 支持同时绑定多个事件的监听器装饰器 * @param events 事件名数组,支持普通事件名、通配符事件名混传 * @param options 监听器配置,和原生@OnEvent配置参数一致 */ export const OnEvents = ( events: string[], options?: Parameters<typeof OnEvent>[1], ) => { return (target: object, propertyKey: string, descriptor: PropertyDescriptor) => { events.forEach(eventName => { OnEvent(eventName, options)(target, propertyKey, descriptor); }); }; };
- 业务代码中直接传入事件数组即可,写法和最初的异常写法基本一致:
@OnEvents( [CONVERSATION_UPDATE, CONVERSATION_DELETE, CONVERSATION_CREATE], { async: true } ) async handleOrderCreatedEvent(payload: ConversationChangeEvent) { await this.lessonService.getAssetById(payload); }
- 适用场景:项目中存在多处多事件绑定需求,需要灵活指定任意事件组合的场景,没有冗余代码,后续扩展方便。
方案3:通配符匹配(仅适合命名规则统一的场景)
如果需要绑定的事件都遵循统一的前缀命名规则,且不存在同前缀不需要监听的事件,可以继续使用通配符方案,注意底层eventemitter2的通配符规则:
*仅匹配单层路径,比如conversation.*可以匹配conversation.create、conversation.update,但无法匹配conversation.member.kick这类多层级事件**匹配任意层级路径,比如conversation.**可以匹配所有以conversation.开头的事件,不受层级限制- 注意:该方案存在新增同前缀事件时被误触发的风险,灵活度低于自定义装饰器方案。
内容的提问来源于stack exchange,提问作者thịnh vũ
相关产品推荐
相关产品推荐

