一、封装
import { ref, onMounted, onUnmounted } from 'vue';
class WebSocketService {
constructor(url) {
this.url = url;
this.socket = null;
this.isAlive = false;
this.reconnectAttempts = 0;
this.MAX_RECONNECT_ATTEMPTS = 5;
this.HEARTBEAT_INTERVAL = 30000;
this.heartbeatTimer = null;
this.reconnectTimer = null;
this.connect();
}
connect() {
this.socket = new WebSocket(this.url);
this.socket.onopen = () => {
console.log('WebSocket connection established');
this.reconnectAttempts = 0;
this.startHeartbeat();
};
this.socket.onmessage = (event) => {
console.log('Message received:', event.data);
};
this.socket.onclose = () => {
console.log('WebSocket connection closed');
this.stopHeartbeat();
this.attemptReconnect();
};
this.socket.onerror = (error) => {
console.error('WebSocket error:', error);
};
}
startHeartbeat() {
if (!this.heartbeatTimer) {
this.heartbeatTimer = setInterval(() => {
if (this.socket.readyState === WebSocket.OPEN) {
this.isAlive = false;
this.sendPing();
}
}, this.HEARTBEAT_INTERVAL);
}
}
stopHeartbeat() {
if (this.heartbeatTimer) {
clearInterval(this.heartbeatTimer);
this.heartbeatTimer = null;
}
}
sendPing() {
this.socket.send(JSON.stringify({ type: 'ping' }));
setTimeout(() => {
if (!this.isAlive) {
console.warn('WebSocket heartbeat failed, attempting to reconnect...');
this.socket.close();
}
}, this.HEARTBEAT_INTERVAL / 2);
}
receivePong() {
this.isAlive = true;
}
attemptReconnect() {
if (this.reconnectAttempts < this.MAX_RECONNECT_ATTEMPTS) {
this.reconnectAttempts++;
console.log(`Attempting to reconnect (${this.reconnectAttempts})...`);
this.reconnectTimer = setTimeout(() => {
this.connect();
}, 1000 * this.reconnectAttempts);
} else {
console.error('Max reconnect attempts reached');
}
}
sendMessage(message) {
if (this.socket && this.socket.readyState === WebSocket.OPEN) {
this.socket.send(JSON.stringify(message));
}
}
close() {
this.stopHeartbeat();
if (this.reconnectTimer) {
clearTimeout(this.reconnectTimer);
this.reconnectTimer = null;
}
if (this.socket) {
this.socket.close();
this.socket = null;
}
}
}
export default function useWebSocket(url) {
const ws = new WebSocketService(url);
const message = ref(null);
const isConnected = ref(false);
const onMessage = (callback) => {
ws.socket.onmessage = (event) => {
message.value = event.data;
callback(event.data);
if (event.data.includes('pong')) {
ws.receivePong();
}
};
};
const onClose = (callback) => {
ws.socket.onclose = () => {
isConnected.value = false;
callback();
};
};
const onOpen = (callback) => {
ws.socket.onopen = () => {
isConnected.value = true;
callback();
};
};
onMounted(() => {
onOpen(() => console.log('Connected'));
onClose(() => console.log('Disconnected'));
onMessage((data) => console.log('Received:', data));
});
onUnmounted(() => {
ws.close();
});
return { isConnected, sendMessage: ws.sendMessage.bind(ws), message };
}
二、使用
import { defineComponent } from 'vue';
import useWebSocket from '@/services/websocketService';
export default defineComponent({
setup() {
const { isConnected, sendMessage, message } = useWebSocket('ws://your-websocket-url');
const handleMessage = (data) => {
console.log('Handling message:', data);
};
return {
isConnected,
sendMessage,
message,
handleMessage,
};
},
});