赞
踩
上篇文章记录了如何在日常开发过程中引入并使用websocket连接,但是在后续的开发过程中发现之前的写法有点问题,比如说多次引用连接会共用一个心跳,如果一个连接关掉了,后续其他的连接可能被一起关掉等等的bug。
所以在这篇文章里针对上篇文章提供的方法进行改进,同时提供兼容vue3写法。
- 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 中更好地使用上述的 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
- };
- }
- });
以上是具体实现方案,在后续开发过程中如果有更好的写法,也会更新本文。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。