上篇文章记录了如何在日常开发过程中引入并使用websocket连接,但是在后续的开发过程中发现之前的写法有点问题,比如说多次引用连接会共用一个心跳,如果一个连接关掉了,后续其他的连接可能被一起关掉等等的bug。

所以在这篇文章里针对上篇文章提供的方法进行改进,同时提供兼容vue3写法。

一、创建 WebSocket 类

class Socket {

constructor(url, opts = {}) {

this.url = url;

this.ws = null;

this.opts = {

heartbeatInterval: 30000, // 默认30秒

reconnectInterval: 5000, // 默认5秒

maxReconnectAttempts: 5, // 默认尝试重连5次

...opts

};

this.reconnectAttempts = 0;

this.listeners = {};

this.init();

}

init() {

this.ws = new WebSocket(this.url);

this.ws.onopen = this.onOpen.bind(this);

this.ws.onmessage = this.onMessage.bind(this);

this.ws.onerror = this.onError.bind(this);

this.ws.onclose = this.onClose.bind(this);

}

onOpen(event) {

console.log('WebSocket opened:', event);

this.reconnectAttempts = 0; // 重置重连次数

this.startHeartbeat();

this.emit('open', event);

}

onMessage(event) {

console.log('WebSocket message received:', event.data);

this.emit('message', event.data);

}

onError(event) {

console.error('WebSocket error:', event);

this.emit('error', event);

}

onClose(event) {

console.log('WebSocket closed:', event);

this.stopHeartbeat();

this.emit('close', event);

if (this.reconnectAttempts < this.opts.maxReconnectAttempts) {

setTimeout(() => {

this.reconnectAttempts++;

this.init();

}, this.opts.reconnectInterval);

}

}

// 发送心跳

startHeartbeat() {

this.heartbeatInterval = setInterval(() => {

if (this.ws.readyState === WebSocket.OPEN) {

this.ws.send('ping'); // 可以修改为你的心跳消息格式

}

}, this.opts.heartbeatInterval);

}

// 停止心跳

stopHeartbeat() {

if (this.heartbeatInterval) {

clearInterval(this.heartbeatInterval);

this.heartbeatInterval = null;

}

}

send(data) {

if (this.ws.readyState === WebSocket.OPEN) {

this.ws.send(data);

} else {

console.error('WebSocket is not open. Cannot send:', data);

}

}

on(event, callback) {

if (!this.listeners[event]) {

this.listeners[event] = [];

}

this.listeners[event].push(callback);

}

off(event, callback) {

if (!this.listeners[event]) return;

const index = this.listeners[event].indexOf(callback);

if (index !== -1) {

this.listeners[event].splice(index, 1);

}

}

emit(event, data) {

if (this.listeners[event]) {

this.listeners[event].forEach(callback => callback(data));

}

}

}

export default Socket;

我们首先定义一个 Socket 类,该类会负责与 WebSocket 服务器建立连接、发送和接收数据、以及管理心跳和重连逻辑。 

在你的Vue组件中使用这个类时,可以这样注册事件:

import Socket from './socket.js';

export default {

data() {

return {

socket: null

};

},

created() {

this.socket = new Socket('ws://your-websocket-url');

this.socket.on('open', event => {

console.log("Connected to server", event);

});

this.socket.on('message', data => {

console.log("Received data:", data);

});

this.socket.on('error', error => {

console.error("WebSocket Error:", error);

});

this.socket.on('close', event => {

console.log("Connection closed", event);

});

},

beforeDestroy() {

// 取消所有事件监听器

this.socket.off('open');

this.socket.off('message');

this.socket.off('error');

this.socket.off('close');

},

methods: {

sendToServer(data) {

this.socket.send(data);

}

}

}

二、使用 Vue 3 的 Composition API

为了在 Vue 3 中更好地使用上述的 Socket 类,我们将其封装为一个 composable 函数,这样可以轻松地在任何 Vue 组件中使用 WebSocket。

import { ref, onUnmounted } from 'vue';

interface SocketOptions {

heartbeatInterval?: number;

reconnectInterval?: number;

maxReconnectAttempts?: number;

}

class Socket {

url: string;

ws: WebSocket | null = null;

opts: SocketOptions;

reconnectAttempts: number = 0;

listeners: { [key: string]: Function[] } = {};

heartbeatInterval: number | null = null;

constructor(url: string, opts: SocketOptions = {}) {

this.url = url;

this.opts = {

heartbeatInterval: 30000,

reconnectInterval: 5000,

maxReconnectAttempts: 5,

...opts

};

this.init();

}

init() {

this.ws = new WebSocket(this.url);

this.ws.onopen = this.onOpen.bind(this);

this.ws.onmessage = this.onMessage.bind(this);

this.ws.onerror = this.onError.bind(this);

this.ws.onclose = this.onClose.bind(this);

}

onOpen(event: Event) {

console.log('WebSocket opened:', event);

this.reconnectAttempts = 0;

this.startHeartbeat();

this.emit('open', event);

}

onMessage(event: MessageEvent) {

console.log('WebSocket message received:', event.data);

this.emit('message', event.data);

}

onError(event: Event) {

console.error('WebSocket error:', event);

this.emit('error', event);

}

onClose(event: CloseEvent) {

console.log('WebSocket closed:', event);

this.stopHeartbeat();

this.emit('close', event);

if (this.reconnectAttempts < this.opts.maxReconnectAttempts!) {

setTimeout(() => {

this.reconnectAttempts++;

this.init();

}, this.opts.reconnectInterval);

}

}

startHeartbeat() {

if (!this.opts.heartbeatInterval) return;

this.heartbeatInterval = window.setInterval(() => {

if (this.ws?.readyState === WebSocket.OPEN) {

this.ws.send('ping');

}

}, this.opts.heartbeatInterval);

}

stopHeartbeat() {

if (this.heartbeatInterval) {

clearInterval(this.heartbeatInterval);

this.heartbeatInterval = null;

}

}

send(data: string) {

if (this.ws?.readyState === WebSocket.OPEN) {

this.ws.send(data);

} else {

console.error('WebSocket is not open. Cannot send:', data);

}

}

on(event: string, callback: Function) {

if (!this.listeners[event]) {

this.listeners[event] = [];

}

this.listeners[event].push(callback);

}

off(event: string) {

if (this.listeners[event]) {

delete this.listeners[event];

}

}

emit(event: string, data: any) {

this.listeners[event]?.forEach(callback => callback(data));

}

}

export function useSocket(url: string, opts?: SocketOptions) {

const socket = new Socket(url, opts);

onUnmounted(() => {

socket.off('open');

socket.off('message');

socket.off('error');

socket.off('close');

});

return {

socket,

send: socket.send.bind(socket),

on: socket.on.bind(socket),

off: socket.off.bind(socket)

};

}

在组件中使用:

import { defineComponent } from 'vue';

import { useSocket } from './useSocket';

export default defineComponent({

name: 'YourComponent',

setup() {

const { socket, send, on, off } = useSocket('ws://your-websocket-url');

on('open', event => {

console.log("Connected to server", event);

});

on('message', data => {

console.log("Received data:", data);

});

on('error', error => {

console.error("WebSocket Error:", error);

});

on('close', event => {

console.log("Connection closed", event);

});

return {

send

};

}

});

三、总结

以上是具体实现方案,在后续开发过程中如果有更好的写法,也会更新本文。

文章链接

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: