如何通过TypeScript装饰器实现Socket.io事件与类方法绑定
使用TypeScript装饰器简化Socket.io事件绑定
我尝试用TypeScript装饰器简化Socket.io逻辑,现有代码如下:
const Event: any = (name) => { return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) { console.log(target, propertyKey, descriptor); }; }; class ServerRoom { private server: Server; @Event("connection") onConnection(socket: Socket) { } } class PlayerRoom { private socket: Socket; constructor(socket: Socket) { this.socket = socket; } @Event("move") onMove(move: any) { console.log("Move: ", move); } @Event("disconnect") onDisconnect() { console.log("disconnected"); } }
我希望实现:
PlayerRoom实例的socket收到move、disconnect事件时,自动调用对应的onMove、onDisconnect方法ServerRoom的server收到connection事件时,自动触发onConnection方法
解决方案
1. 改造Event装饰器,存储事件与方法映射
首先需要一个注册表来记录每个类的事件和对应方法的关联关系,在装饰器执行时填充这个注册表:
// 注册表:键为类原型,值为事件-方法映射数组 const eventRegistry = new Map<any, Array<{ eventName: string; methodName: string }>>(); // 类型化的Event装饰器 const Event = (eventName: string) => { return function (target: any, propertyKey: string) { let events = eventRegistry.get(target); if (!events) { events = []; eventRegistry.set(target, events); } // 存入当前事件与方法名的映射 events.push({ eventName, methodName: propertyKey }); }; };
2. 实现PlayerRoom的事件自动绑定
在PlayerRoom的构造函数中,从注册表取出事件映射,给socket绑定对应的方法(注意绑定this上下文):
class PlayerRoom { private socket: Socket; constructor(socket: Socket) { this.socket = socket; // 初始化事件绑定 this.bindEvents(); } private bindEvents() { const events = eventRegistry.get(PlayerRoom.prototype); if (!events) return; events.forEach(({ eventName, methodName }) => { // 绑定事件回调,确保方法内的this指向当前实例 this.socket.on(eventName, (...args) => { (this as any)[methodName](...args); }); }); } @Event("move") onMove(move: any) { console.log("Move: ", move); } @Event("disconnect") onDisconnect() { console.log("disconnected"); } }
3. 实现ServerRoom的事件自动绑定
同理,在ServerRoom的构造函数中完成server的事件绑定:
class ServerRoom { private server: Server; constructor(server: Server) { this.server = server; // 初始化事件绑定 this.bindEvents(); } private bindEvents() { const events = eventRegistry.get(ServerRoom.prototype); if (!events) return; events.forEach(({ eventName, methodName }) => { this.server.on(eventName, (...args) => { (this as any)[methodName](...args); }); }); } @Event("connection") onConnection(socket: Socket) { // 新连接建立时创建PlayerRoom实例 new PlayerRoom(socket); } }
4. TypeScript配置要求
要启用装饰器特性,需在tsconfig.json中添加以下配置:
{ "compilerOptions": { "experimentalDecorators": true, "target": "ES6" // 或更高版本,确保支持装饰器语法 } }
内容的提问来源于stack exchange,提问作者Andrea D'Attero
相关产品推荐
相关产品推荐

