JavaScript封装WebSocket类如何实现可对外监听的自定义事件触发?
实现方案
核心实现思路
让自定义类继承浏览器原生的EventTarget接口,这个接口原生提供了addEventListener、removeEventListener、dispatchEvent三个方法,完全符合你期望的调用方式,不需要额外引入第三方依赖,兼容性也足够现代前端项目使用。
另外需要先修复原有代码的两个隐含问题:
- WebSocket的
send方法不能直接发送JS对象,需要先序列化为JSON字符串 - 接收消息时拿到的
messageEvent.data才是实际消息内容,需要反序列化为JS对象再传递
修改后的完整代码
class Multiplayer extends EventTarget { constructor(url, port, useSecure = false) { // 调用父类EventTarget的构造函数 super(); const protocol = useSecure ? "wss://" : "ws://"; this.socket = new WebSocket(`${protocol}${url}:${port}`); this.opponentReady = false; this.ready = false; this.recvEventMsg = "receivedMessage"; // 消息队列:连接未建立时暂存要发送的消息 this.msgQueue = []; this.openConnection(); this.receivedMessage(); this.handleError(); this.handleClose(); } closeConnection() { this.socket.close(); } getCustomEventName() { return this.recvEventMsg; } openConnection() { this.socket.addEventListener("open", (event) => { console.log("WebSocket连接已建立"); this.ready = true; // 发送暂存的消息队列 this.msgQueue.forEach(msg => this.socket.send(msg)); this.msgQueue = []; this.socket.send("Let's play"); this.opponentReady = true; // 抛出连接成功事件 this.dispatchEvent(new CustomEvent('connected', { detail: event })); }); } sendMessage(message, status) { const contents = JSON.stringify({ msg: message, status: status, }); // 连接未建立时先放入队列 if (this.socket.readyState !== WebSocket.OPEN) { this.msgQueue.push(contents); return true; } this.socket.send(contents); return true; } receivedMessage() { this.socket.addEventListener("message", (messageEvent) => { console.log("收到消息:" + messageEvent.data); let parsedData; try { parsedData = JSON.parse(messageEvent.data); } catch (e) { // 非JSON格式的消息直接返回原始内容 parsedData = messageEvent.data; } // 抛出自定义接收消息事件 const newEvent = new CustomEvent(this.recvEventMsg, { detail: { message: parsedData } }); this.dispatchEvent(newEvent); }); } handleError() { this.socket.addEventListener('error', (error) => { // 抛出错误事件 this.dispatchEvent(new CustomEvent('error', { detail: error })); }); } handleClose() { this.socket.addEventListener('close', (event) => { this.ready = false; this.opponentReady = false; // 抛出连接关闭事件 this.dispatchEvent(new CustomEvent('closed', { detail: event })); }); } }
调用示例
完全符合你期望的使用方式:
const socketConnectionEvent = (eventMessage) => { console.log('Socket Event has been captured!'); console.log('Player said: ', eventMessage.detail.message); } const handleConnected = () => { console.log('连接成功,可以开始发送消息'); } let socketConnection = new Multiplayer('127.0.0.1', 8000); socketConnection.addEventListener(socketConnection.getCustomEventName(), socketConnectionEvent); socketConnection.addEventListener('connected', handleConnected);
可复用优化建议
- 可添加自动重连配置,传入重试次数、重试间隔参数,断线后自动尝试重连
- 可支持传入自定义事件名称前缀,避免多个实例同时使用时的事件名冲突
- 可添加心跳检测逻辑,定时发送心跳包判断连接是否正常存活
- 可支持销毁方法,清除所有事件监听和定时器,避免内存泄漏
内容的提问来源于stack exchange,提问作者Fifth Cloud
相关产品推荐
相关产品推荐

