对 websocket 进行封装 (心跳检测 断开重连 发送事件等等 支持断开重连后发送上次发送的最后一个事件)
代码封装:
// websocketService.js import { ref } from "vue"; const { DEV, VITE_APP_PUSH_WS } = import.meta.env; const { push_ws } = window.WEB_CONFIG; const baseWsUrl = DEV ? VITE_APP_PUSH_WS : push_ws; class WebSocketService { constructor(url) { this.url = url; this.socket = null; this.reconnectTimer = null; this.heartbeatTimer = null; this.socketStatus = false; this.reconnectInterval = 5000; // 重连间隔时间 this.heartbeatInterval = 30000; // 心跳间隔时间 this.websocketObj = ref(null); this.onOpenCallback = null; // 新增用于保存连接成功后的回调函数 this.eventListeners = {}; // 用于存储事件监听器 this.lastSentMessage = null; // 存储最后发送的消息 } initSocket(onOpenCallback) { this.socket = new WebSocket(this.url); this.socket.onopen = () => { clearInterval(this.reconnectTimer); this.socketStatus = true; this.startHeartbeat(); this.websocketObj.value = this.socket; if (onOpenCallback) { onOpenCallback(); } if (this.lastSentMessage) { this.sendMessage(this.lastSentMessage); // 重连后发送最后一条消息 } }; this.socket.onmessage = (event) => { const data = JSON.parse(event.data); const token = window.localStorage.getItem('token') || ''; if (!token) { this.closeSocket(); } if (this.eventListeners[data.event]) { this.eventListeners[data.event].forEach(callback => callback(data)); } }; this.socket.onclose = () => { this.handleSocketClose(); }; this.socket.onerror = () => { this.handleSocketError(); }; } startHeartbeat() { this.heartbeatTimer = setInterval(() => { this.socket.send(JSON.stringify({ event: "heartbeat" })); }, this.heartbeatInterval); } stopHeartbeat() { clearInterval(this.heartbeatTimer); } closeSocket() { this.stopHeartbeat(); this.socket.close(); } handleSocketClose() { console.log("WebSocket closed"); this.socketStatus = false; this.stopHeartbeat(); this.reconnect(); } handleSocketError() { console.log("WebSocket error"); this.socketStatus = false; this.stopHeartbeat(); this.reconnect(); } reconnect() { if (this.reconnectTimer) return; this.reconnectTimer = setInterval(() => { this.initSocket(this.onOpenCallback); }, this.reconnectInterval); } sendMessage(message) { this.lastSentMessage = message; // 存储最后发送的消息 if (this.socket && this.socket.readyState === WebSocket.OPEN) { this.socket.send(message); } else { console.error('WebSocket is not open. Ready state is: ' + (this.socket ? this.socket.readyState : 'N/A')); } } addEventListener(event, callback) { if (!this.eventListeners[event]) { this.eventListeners[event] = []; } this.eventListeners[event].push(callback); } removeEventListener(event, callback) { if (this.eventListeners[event]) { this.eventListeners[event] = this.eventListeners[event].filter(cb => cb !== callback); } } } const wsUrl = `${baseWsUrl}/ws/footballMsg`; const websocketService = new WebSocketService(wsUrl); export const newwebsocketObj = websocketService.websocketObj; export const initWebsocketObj = (onOpenCallback) => { websocketService.initSocket(onOpenCallback); }; export const sendMessage = (message) => { websocketService.sendMessage(message); }; export const closeWebsocket = () => { websocketService.closeSocket(); }; export const addEventListener = (event, callback) => { websocketService.addEventListener(event, callback); }; export const removeEventListener = (event, callback) => { websocketService.removeEventListener(event, callback); };
代码使用:
<script setup> import { ref, onMounted, onUnmounted, watch } from 'vue'; import { initWebsocketObj, sendMessage, addEventListener, removeEventListener, websocketObj } from './websocketService.js'; const tableData = ref([]); const standardMatchId = ref('match_id_example'); // 这是一个示例的 match_id,你可以从路由或其他地方获取 // 处理 WebSocket 消息的回调函数 const handleFootballMessageEvent = (data) => { if (data.event === 'FootballMessageEvent') { const { id = '' } = data.payload || {}; if (!tableData.value.length || !id.startsWith(standardMatchId.value)) return; tableData.value.unshift(data.payload); tableData.value = [...new Set(tableData.value.map(JSON.stringify))].map( JSON.parse ); } }; onMounted(() => { // 初始化 WebSocket initWebsocketObj(() => { // 连接成功后发送初始消息 const message = JSON.stringify({ event: "subMatch", payload: { FootballMessageEvent: [standardMatchId.value] } }); sendMessage(message); }); // 添加事件监听器 addEventListener('FootballMessageEvent', handleFootballMessageEvent); // 监听 tableData 的变化 watch(tableData, (newValue) => { const standardMatchIds = newValue.map(item => item.standard_match_id + ''); const params = { BetMarketEvent: standardMatchIds, FootballEvent: standardMatchIds }; const message = JSON.stringify({ event: "subMatch", payload: params }); sendMessage(message); }); // 组件卸载时移除事件监听器 onUnmounted(() => { removeEventListener('FootballMessageEvent', handleFootballMessageEvent); websocketObj.value && websocketObj.value.close(); }); }); </script> <template> <div> <table> <thead> <tr> <th>Match ID</th> <th>Event</th> </tr> </thead> <tbody> <tr v-for="item in tableData" :key="item.standard_match_id"> <td>{{ item.standard_match_id }}</td> <td>{{ item.event }}</td> </tr> </tbody> </table> </div> </template>
越努力越幸运