上篇文章记录了如何在日常开发过程中引入并使用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)
};
}
在组件中使用:文章来源:https://www.toymoban.com/news/detail-731453.html
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
};
}
});
三、总结
以上是具体实现方案,在后续开发过程中如果有更好的写法,也会更新本文。文章来源地址https://www.toymoban.com/news/detail-731453.html
到了这里,关于在vue3中封装使用WebSocket的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!