You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.01 21:48:02