以下是WebsocketMixin.js内容,简单易懂,复制之后基本只需要修改地址拼接方式和心跳格式。

// WebsocketMixin.js
import store from '@/store/'
import { ACCESS_TOKEN } from '@/store/mutation-types'
import Vue from 'vue'

export const WebsocketMixin = {
  data() {
    return {
      heartbeatTimer: null,         // 心跳定时器
      heartbeatTimeoutTimer: null,  // 心跳超时检测定时器
      reconnectTimer: null,         // 重连定时器
      heartbeatInterval: 30000,     // 心跳间隔 30秒
      heartbeatTimeout: 10000,      // 心跳超时时间 10秒(等待服务端响应)
      lockReconnect: false,         // 重连锁
      isManualClose: false,         // 是否手动关闭
      heartbeatPending: false,      // 是否有心跳等待响应
      heartbeatFailCount: 0,        // 心跳连续失败次数
      maxHeartbeatFail: 2,          // 最大连续失败次数,超过则重连
      _originalOnMessage: null      
    }
  },
  mounted() {
    this.initWebSocket();
  },
  destroyed: function () {
    this.websocketOnclose();
    // 清理所有定时器
    this.clearAllTimers();
  },
  methods: {
    initWebSocket: function () {
      let token = Vue.ls.get(ACCESS_TOKEN)
      console.log("------------WebSocket连接准备");
      // 获取用户ID
      var userId = store.getters.userInfo.id;
      if (!this.socketUrl.startsWith('/')) {
        this.socketUrl = '/' + this.socketUrl
      }
      if (!this.socketUrl.endsWith('/')) {
        this.socketUrl = this.socketUrl + '/'
      }
      
      var url = window._CONFIG['domianURL']
        .replace("https://", "wss://")
        .replace("http://", "ws://") 
        + this.socketUrl + userId 
      
      console.log("WebSocket URL:", url);
      
      this.isManualClose = false;
      this.heartbeatPending = false;
      this.heartbeatFailCount = 0;
      
    
      if (this.websocketOnmessage && typeof this.websocketOnmessage === 'function') {
        this._originalOnMessage = this.websocketOnmessage.bind(this);
      }
      
      this.websock = new WebSocket(url, [token]);
      this.websock.onopen = this.websocketOnopen;
      this.websock.onerror = this.websocketOnerror;
      // 使用 Mixin 的 onmessage 处理
      this.websock.onmessage = this._handleMessage.bind(this);
      this.websock.onclose = this.websocketOnclose;
    },
    // 统一的 message 处理入口
    _handleMessage: function (event) {
      try {
        // 先尝试解析JSON
        let data = event.data;
        let parsedData = null;
        
        try {
          parsedData = JSON.parse(data);
        } catch (e) {
          // 不是JSON格式,可能是文本消息
          parsedData = data;
        }
        
        // ===== 1. 处理心跳响应(Mixin处理,不传递给组件) =====
        // 判断是否为心跳响应:{"cmd":"heartcheck","msgTxt":"心跳响应"}
        if (parsedData && typeof parsedData === 'object' && parsedData.cmd === 'heartcheck') {
          console.log("收到心跳响应:", parsedData.msgTxt || '心跳响应');
          this.heartbeatPending = false;
          this.heartbeatFailCount = 0;
          this.resetHeartbeatTimeout();
          return; // 心跳响应不传递给组件
        }
        
        // ===== 2. 处理文本心跳响应 =====
        if (data === 'pong' || data === 'heartbeat_ack') {
          console.log("收到心跳响应:", data);
          this.heartbeatPending = false;
          this.heartbeatFailCount = 0;
          this.resetHeartbeatTimeout();
          return; // 心跳响应不传递给组件
        }
        
        // ===== 3. 业务消息传递给组件的 websocketOnmessage =====
        if (this._originalOnMessage) {
          // 保持原始 event 对象
          this._originalOnMessage(event);
        } else if (this.websocketOnmessage && typeof this.websocketOnmessage === 'function') {
          // 如果组件定义了 websocketOnmessage,直接调用
          this.websocketOnmessage(event);
        } else {
          // 没有业务处理器,仅打印日志
          console.log("WebSocket收到消息(未处理):", data);
        }
        
      } catch (err) {
        console.error('_handleMessage error:', err);
      }
    },
    websocketOnopen: function () {
      console.log("WebSocket连接成功");
      // 重置重连标志
      this.lockReconnect = false;
      this.heartbeatFailCount = 0;
      // 启动心跳
      this.startHeartbeat();
    },
    websocketOnerror: function (e) {
      console.error("WebSocket连接发生错误:", e);
      this.stopHeartbeat();
      this.stopHeartbeatTimeout();
      this.reconnect();
    },
    websocketOnclose: function (e) {
      console.log("WebSocket连接关闭:", e);
      this.stopHeartbeat();
      this.stopHeartbeatTimeout();
      // 如果是手动关闭,不触发重连
      if (!this.isManualClose) {
        this.reconnect();
      }
    },
    websocketSend(text) {
      try {
        if (this.websock && this.websock.readyState === WebSocket.OPEN) {
          this.websock.send(text);
          // 发送数据后重置心跳计时器,避免不必要的频繁心跳
          this.resetHeartbeat();
        } else {
          console.warn("WebSocket未连接,无法发送消息");
          // 尝试重连
          this.reconnect();
        }
      } catch (err) {
        console.error("send failed:", err);
      }
    },
    reconnect() {
      var that = this;
      if (that.lockReconnect) return;
      that.lockReconnect = true;
      // 清除之前的重连定时器
      if (that.reconnectTimer) {
        clearTimeout(that.reconnectTimer);
        that.reconnectTimer = null;
      }
      // 避免重复重连导致请求过多
      that.reconnectTimer = setTimeout(function () {
        console.info("尝试重连...");
        // 先关闭现有连接
        if (that.websock) {
          that.websock.close();
        }
        that.initWebSocket();
        setTimeout(() => {
          that.lockReconnect = false;
        }, 1000);
      }, 5000);
    },
    // 启动心跳
    startHeartbeat() {
      this.stopHeartbeat(); // 先清除旧定时器
      this.stopHeartbeatTimeout();
      this.heartbeatPending = false;
      this.heartbeatFailCount = 0;
      
      this.heartbeatTimer = setInterval(() => {
        if (this.websock && this.websock.readyState === WebSocket.OPEN) {
          // 发送心跳消息(ping)
          const pingMsg = JSON.stringify({ cmd: 'ping' });
          this.websock.send(pingMsg);
          console.log("发送心跳 ping");
          
          // 标记心跳等待响应
          this.heartbeatPending = true;
          
          // 启动心跳超时检测
          this.startHeartbeatTimeout();
          
        } else {
          console.warn("WebSocket未连接,停止心跳");
          this.stopHeartbeat();
          this.stopHeartbeatTimeout();
          // 如果连接断开了,尝试重连
          if (!this.isManualClose) {
            this.reconnect();
          }
        }
      }, this.heartbeatInterval);
    },
    // 停止心跳
    stopHeartbeat() {
      if (this.heartbeatTimer) {
        clearInterval(this.heartbeatTimer);
        this.heartbeatTimer = null;
      }
    },
    // 启动心跳超时检测
    startHeartbeatTimeout() {
      this.stopHeartbeatTimeout();
      this.heartbeatTimeoutTimer = setTimeout(() => {
        // 如果心跳超时且还在等待响应
        if (this.heartbeatPending) {
          console.warn("心跳超时,未收到服务端响应");
          this.heartbeatFailCount++;
          
          if (this.heartbeatFailCount >= this.maxHeartbeatFail) {
            console.error(`心跳连续失败${this.heartbeatFailCount}次,触发重连`);
            this.heartbeatPending = false;
            this.heartbeatFailCount = 0;
            // 触发重连
            if (!this.isManualClose) {
              this.stopHeartbeat();
              this.stopHeartbeatTimeout();
              this.reconnect();
            }
          } else {
            // 继续等待下一次心跳
            this.heartbeatPending = false;
            console.log(`心跳失败次数: ${this.heartbeatFailCount}/${this.maxHeartbeatFail}`);
          }
        }
      }, this.heartbeatTimeout);
    },
    // 停止心跳超时检测
    stopHeartbeatTimeout() {
      if (this.heartbeatTimeoutTimer) {
        clearTimeout(this.heartbeatTimeoutTimer);
        this.heartbeatTimeoutTimer = null;
      }
    },
    // 重置心跳超时检测
    resetHeartbeatTimeout() {
      // 如果还有心跳在等待,重置超时计时器
      if (this.heartbeatPending) {
        this.startHeartbeatTimeout();
      }
    },
    // 重置心跳
    resetHeartbeat() {
      // 收到消息或发送消息后重置心跳计时器
      if (this.heartbeatTimer) {
        clearInterval(this.heartbeatTimer);
        this.heartbeatTimer = setInterval(() => {
          if (this.websock && this.websock.readyState === WebSocket.OPEN) {
            // 发送心跳消息(ping)
            const pingMsg = JSON.stringify({ cmd: 'ping' });
            this.websock.send(pingMsg);
            console.log("发送心跳 ping");
            this.heartbeatPending = true;
            this.startHeartbeatTimeout();
          } else {
            console.warn("WebSocket未连接,停止心跳");
            this.stopHeartbeat();
            this.stopHeartbeatTimeout();
            if (!this.isManualClose) {
              this.reconnect();
            }
          }
        }, this.heartbeatInterval);
      }
    },
    // 清理所有定时器
    clearAllTimers() {
      this.stopHeartbeat();
      this.stopHeartbeatTimeout();
      if (this.reconnectTimer) {
        clearTimeout(this.reconnectTimer);
        this.reconnectTimer = null;
      }
    },
    // 手动关闭WebSocket(可外部调用)
    closeWebSocket() {
      this.isManualClose = true;
      this.stopHeartbeat();
      this.stopHeartbeatTimeout();
      if (this.websock) {
        this.websock.close();
      }
    }
  }
}

使用方法:

1、页面引入

import { WebsocketMixin } from '@/mixins/WebsocketMixin' 

2、页面注册

export default 中增加
mixins: [WebsocketMixin]

3、data中定义地址

socketUrl: 'websocket', 

4、methods中写接收方法

websocketOnmessage: function (e) {
  console.log("WebSocket收到消息:", e.data);
  try {
    const data = e.data;
  //你的业务逻辑
   
  } catch (err) {
    console.log('websocketOnmessage error:', err);
  }
},

至此结束。

更多推荐