113 lines
3.5 KiB
TypeScript
113 lines
3.5 KiB
TypeScript
// src/utils/websocket.ts
|
|
import {onUnmounted} from 'vue';
|
|
|
|
interface WebSocketOptions {
|
|
url: string;
|
|
protocols?: string | string[];
|
|
reconnectTimeout?: number;
|
|
}
|
|
|
|
type MessageCallback = (data: any) => void;
|
|
type EventCallback = () => void;
|
|
|
|
class WebSocketService {
|
|
private ws: WebSocket | null = null;
|
|
private callbacks: { [key: string]: (MessageCallback | EventCallback)[] } = {};
|
|
private reconnectTimeoutMs: number = 5000; // 默认5秒重连间隔
|
|
private heartbeatIntervalMs: number = 5000; // 默认5秒心跳间隔
|
|
|
|
constructor(private options: WebSocketOptions) {
|
|
}
|
|
|
|
public open(): void {
|
|
this.ws = new WebSocket(this.options.url, this.options.protocols);
|
|
this.ws.addEventListener('open', this.handleOpen);
|
|
this.ws.addEventListener('message', this.handleMessage);
|
|
this.ws.addEventListener('error', this.handleError);
|
|
this.ws.addEventListener('close', this.handleClose);
|
|
setInterval(() => {
|
|
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
|
|
this.send('ping');
|
|
}
|
|
}, this.heartbeatIntervalMs);
|
|
}
|
|
|
|
public close(isActiveClose = false): void {
|
|
if (this.ws) {
|
|
this.ws.close();
|
|
if (!isActiveClose) {
|
|
setTimeout(() => this.reconnect(), this.reconnectTimeoutMs);
|
|
}
|
|
}
|
|
}
|
|
|
|
public reconnect(): void {
|
|
this.open();
|
|
}
|
|
|
|
public on(event: 'message', callback: MessageCallback): void;
|
|
public on(event: 'open' | 'error' | 'close', callback: EventCallback): void;
|
|
public on(event: string, callback: (...args: any[]) => void): void {
|
|
if (!this.callbacks[event]) {
|
|
this.callbacks[event] = [];
|
|
}
|
|
this.callbacks[event].push(callback);
|
|
}
|
|
|
|
private handleOpen = (): void => {
|
|
console.log('WebSocket连接已建立');
|
|
if (this.callbacks.open) {
|
|
this.callbacks.open.forEach((cb) => (cb as EventCallback)());
|
|
}
|
|
};
|
|
|
|
private handleMessage = (event: MessageEvent): void => {
|
|
const data = JSON.parse(event.data);
|
|
console.log('WebSocket接收到消息:', data);
|
|
if (this.callbacks.message) {
|
|
this.callbacks.message.forEach((cb) => (cb as MessageCallback)(data));
|
|
}
|
|
};
|
|
|
|
private handleError = (error: Event): void => {
|
|
console.error('WebSocket错误:', error);
|
|
if (this.callbacks.error) {
|
|
this.callbacks.error.forEach((cb) => (cb as EventCallback)());
|
|
}
|
|
};
|
|
|
|
private handleClose = (): void => {
|
|
console.log('WebSocket连接已关闭');
|
|
if (this.callbacks.close) {
|
|
this.callbacks.close.forEach((cb) => (cb as EventCallback)());
|
|
if (!this.options.reconnectTimeout) {
|
|
this.reconnect();
|
|
}
|
|
}
|
|
};
|
|
|
|
public send(data: any): void {
|
|
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
|
|
this.ws.send(JSON.stringify(data));
|
|
} else {
|
|
console.warn('尝试发送消息时WebSocket未连接');
|
|
}
|
|
}
|
|
}
|
|
|
|
export default function useWebSocket(options: WebSocketOptions) {
|
|
const wsService = new WebSocketService(options);
|
|
|
|
onUnmounted(() => {
|
|
wsService.close(true);
|
|
});
|
|
|
|
return {
|
|
open: wsService.open.bind(wsService),
|
|
close: wsService.close.bind(wsService),
|
|
reconnect: wsService.reconnect.bind(wsService),
|
|
on: wsService.on.bind(wsService),
|
|
send: wsService.send.bind(wsService)
|
|
};
|
|
}
|