WebSocket封装(TypeScript、单例模式、自动重连、事件监听、Vue中使用)

export type AutoReconnectOptions = boolean | {
  maxRetries?: number
  retryInterval?: number
  onMaxRetriesReached?: Function
}

export enum ConnectionStatus {
  Disconnected = 'DISCONNECTED',
  Connected = 'CONNECTED',
  Error = 'ERROR'
}

class SocketService {
  private static instance: SocketService
  private ws: WebSocket | null = null
  private listeners: Record<string, Function[]> = {}
  private autoReconnect: AutoReconnectOptions = true
  private retries: number = 0
  private connectionStatus: ConnectionStatus = ConnectionStatus.Disconnected

  private constructor() {
    this.connect()
  }

  public static getInstance(): SocketService {
    if (!SocketService.instance) {
      SocketService.instance = new SocketService()
    }
    return SocketService.instance
  }

  public setAutoReconnectOptions(options: AutoReconnectOptions) {
    this.autoReconnect = options
  }

  public connect() {
    this.ws = new WebSocket('ws://localhost:3000/ws')
    this.ws.onopen = () => {
      this.connectionStatus = ConnectionStatus.Connected
      this.emit('connected', null)
    }
    this.ws.onerror = () => {
      this.connectionStatus = ConnectionStatus.Error
      this.emit('error', null)
    }
    this.ws.onclose = () => {
      this.connectionStatus = ConnectionStatus.Disconnected
      this.emit('disconnected', null)
      if (this.shouldReconnect()) {
        setTimeout(() => this.connect(), this.getRetryInterval())
      }
    }
    this.ws.onmessage = (event) => {
      this.emit('message', event.data)
    }
  }

  private shouldReconnect(): boolean {
    if (typeof this.autoReconnect === 'boolean') {
      return this.autoReconnect
    } else if (this.autoReconnect) {
      const { maxRetries } = this.autoReconnect

      if (maxRetries !== undefined) {
        if (this.retries < maxRetries) {
          this.retries++
          return true
        } else if (this.retries >= maxRetries) {
          this.autoReconnect.onMaxRetriesReached && this.autoReconnect.onMaxRetriesReached()
          return false
        }
      }
    }
    return false
  }

  private getRetryInterval(): number {
    if (typeof this.autoReconnect === 'boolean') {
      return 1000
    } else if (this.autoReconnect && this.autoReconnect.retryInterval) {
      return this.autoReconnect.retryInterval
    }
    return 1000
  }

  public send(data: any) {
    if (this.ws && this.ws.readyState === WebSocket.OPEN) {
      this.ws.send(JSON.stringify(data))
    } else {
      console.error('WebSocket 连接未打开')
    }
  }

  public close() {
    if (this.ws) {
      this.ws.close()
    }
  }

  private emit(event: string, data: any) {
    if (!this.listeners[event]) {
      return
    }
    this.listeners[event].forEach((listener) => listener(data))
  }

  public on(event: string, listener: Function) {
    if (!this.listeners[event]) {
      this.listeners[event] = []
    }
    this.listeners[event].push(listener)
    if (event === 'connected' && this.connectionStatus === ConnectionStatus.Connected) {
      listener()
    }
  }

  public off(event: string, listener: Function) {
    if (!this.listeners[event]) {
      return
    }
    this.listeners[event] = this.listeners[event].filter((l) => l !== listener)
  }

  public getConnectionStatus(): ConnectionStatus {
    return this.connectionStatus
  }

  public watchConnectionStatus(callback: (status: ConnectionStatus) => void) {
    this.on('connected', () => callback(ConnectionStatus.Connected))
    this.on('disconnected', () => callback(ConnectionStatus.Disconnected))
    this.on('error', () => callback(ConnectionStatus.Error))
  }
}

export default SocketService

在 Vue3 中使用:

<script setup lang="ts">
import { onBeforeUnmount, ref } from 'vue'
import SocketService from '@/lib/SocketService'

const socketService = SocketService.getInstance()

const connectionStatus = ref(socketService.getConnectionStatus())
socketService.watchConnectionStatus((status) => {
  connectionStatus.value = status
})

socketService.setAutoReconnectOptions({
  maxRetries: 4,
  retryInterval: 2000,
  onMaxRetriesReached: () => {
    console.log('max retries reached')
  }
})

const onConnected = () => {
  console.log('connected')
}
socketService.on('connected', onConnected)

const onDisconnected = () => {
  console.log('disconnected')
}
socketService.on('disconnected', onDisconnected)

const onMessage = (data: any) => {
  console.log('message', data)
}
socketService.on('message', onMessage)

onBeforeUnmount(() => {
  socketService.off('connected', onConnected)
  socketService.off('disconnected', onDisconnected)
  socketService.off('message', onMessage)
})
</script>

<template>
  <div>
    <div>Connection status: {{ connectionStatus }}</div>
    <button @click="socketService.connect">Connect</button>
    <button @click="socketService.close">Close</button>
    <button @click="socketService.send('Hello')">Send</button>
  </div>
</template>

可以在不同的 Vue 组件/页面中通过 SocketService.getInstance() 获取同一个 WebSocket 连接,监听数据接收以及发送消息。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/571406.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

什么是redis服务+redis服务数据类型有哪些??

一、背景&#xff1a; 在运维工作会一定会接触到数据库服务&#xff0c;例如oracle数据库、mysql数据库、redis数据库等&#xff0c;这里要介绍的就是redis数据库。 二、什么是redis&#xff1f;&#xff1f; Redis&#xff0c;英文全称是Remote Dictionary Server&#xff08;…

GraphQL速学笔记

在学习开始前&#xff0c;我习惯先用gpt了解一个这是个什么东西&#xff1a; GraphQL是一种用于API开发的查询语言和运行时环境。它由Facebook于2012年开发并在2015年开源&#xff0c;旨在解决传统RESTful API的一些限制和缺点。 在GraphQL中&#xff0c;客户端可以通过发送查询…

STM32F103学习笔记 | 4.STM32F103芯片介绍

STM32F1入门学习将使用STM32F103C8T6开发板最小系统板。小R为什么选择它来入门呢&#xff1f;咳咳~首先&#xff0c;ST官方提供强大且易用的标准库函数&#xff0c;使得开发过程方便快捷&#xff1b;其次&#xff0c;网上的教程资料多也十分详细。所以呢&#xff0c;它对高校学…

STM32 串口打印乱码(Cubemx)

STM32 串口打印乱码&#xff08;Cubemx&#xff09; 时钟配置错误&#xff0c;CubeMX默认的外部晶振是25MHz&#xff0c;而板载的晶振为8MHzSTM32F407修改程序将外部25M晶振修改为8M&#xff08;标准库、HAL库&#xff09; 核心问题 芯片型号与晶振配置&#xff1a;使用的ST…

深入探究音视频开源库WebRTC中NetEQ音频抗网络延时与抗丢包的实现机制

目录 1、引言 2、WebRTC简介 3、什么是NetEQ&#xff1f; 4、NetEQ技术详解 4.1、NetEQ概述 4.2、抖动消除技术 4.3、丢包补偿技术 4.4、NetEQ概要设计 4.5、NetEQ的命令机制 4.6、NetEQ的播放机制 4.7、MCU的控制机制 4.8、DSP的算法处理 4.9、DSP算法的模拟测试…

Redis之路系列(5)功夫在诗外

5 拓展篇—功夫在诗外 6.0新特性 相对都比较鸡肋&#xff0c;谨慎在生产环境使用 ACL安全策略 Redis6版本推出了ACL(Access Control List)访问控制权限 的功能&#xff0c;基于此功能&#xff0c;可以设置多个用户&#xff0c;并且给每个用户单独设 置命令权限和数据权限。 …

【Linux高性能服务器编程】两种高性能并发模式剖析——领导者/追随者模式

hello &#xff01;大家好呀&#xff01; 欢迎大家来到我的Linux高性能服务器编程系列之两种高性能并发模式介绍&#xff0c;在这篇文章中&#xff0c;你将会学习到高效的创建自己的高性能服务器&#xff0c;并且我会给出源码进行剖析&#xff0c;以及手绘UML图来帮助大家来理解…

【行为型模式】中介者模式

一、中介者模式概述 中介者模式定义&#xff1a;用一个中介对象来封装一系列的对象交互&#xff0c;中介者使各对象不需要显式地相互引用&#xff0c;从而使其耦合松散&#xff0c;而且可以独立地改变它们之间的交互。中介者模式又称为调停者模式。(对象行为型模式) 中介者模式…

Web3与物联网:探索区块链如何驱动智能设备的未来

引言 在数字化快速发展的时代&#xff0c;Web3技术和物联网&#xff08;IoT&#xff09;都成为了前沿技术的代表。两者的结合正逐渐展现出无限的可能性&#xff0c;尤其是在智能设备和数据安全方面。本文将深入探讨Web3如何与物联网相结合&#xff0c;以及这种结合对未来智能设…

有效三角形的个数 ---- 双指针

题目链接 题目: 分析: 这道题的意思就是将数组的元素, 拿出三个数, 能构成三角形就是有效的判断是否能构成三角形的条件: 两边之和大于第三边, 我们只需找到三个数中最小的两个数之和是否大于第三边, 大于则可以构成三角形解法一: 暴力解法, 即找到所有的三元组, 并挨个判断,…

分布式与一致性协议之CAP(二)

CAP CAP不可能三角 CAP不可能三角是指对于一个分布式系统而言&#xff0c;一致性、可用性、分区容错性指标不可兼得&#xff0c;只能从中选择两个&#xff0c; 如图所示。CAP不可能三角最初是埃里克布鲁尔(Eric Brewer)基于自己的工程实践提出的一个猜想&#xff0c;后被塞斯吉…

【C语言 |预处理指令】预处理指令详解(包括编译与链接)

目录 一、编译与链接 1.翻译环境 -预处理 -编译 -汇编 -链接 2.执行环境 二、预定义符号 三、#define定义常量 四、#define定义宏 五、带有副作用的宏参数 六、宏替换的规则 七、 宏函数的对比 八、#和## 1.#运算符 2.##运算符 九、命名约定 十、#undef 十一、 命…

【03-掌握Scikit-learn:深入机器学习的实用技术】

文章目录 前言数据预处理缺失值处理数据缩放特征选择模型训练参数调整模型评估总结前言 经过了对Python和Scikit-learn的基础安装及简单应用,我们现在将更深入地探究Scikit-learn的实用技术,以进一步提升我们的数据科学技能。在本文中,我们将涵盖数据预处理、特征选择、模型…

【唯美情侣爱情表白纪念HTML单页】

唯美情侣爱情表白纪念HTML单页 效果图部分代码领取代码下期更新预报 效果图 整图 背景图 部分代码 index.html <!DOCTYPE html> <html lang"en"><head><meta http-equiv"Content-Type" content"text/html; charsetUTF-8"…

YOLOv8 实现车牌检测,生成可视化检测视频(20240424)

原项目源码地址&#xff1a;GitHub 我的源码地址&#xff1a;Gitee 环境搭建请参考&#xff1a;Win10 搭建 YOLOv8 运行环境&#xff08;20240423&#xff09;-CSDN博客 环境测试请参考&#xff1a;本地运行测试 YOLOv8&#xff08;20240423&#xff09;-CSDN博客 训练数据…

《系统架构设计师教程(第2版)》第15章-面向服务架构设计理论与实践-05-SOA设计模式

文章目录 1. 服务注册表模式1.1 服务注册表1.2 SOA治理功能1.3 注册表中的配置文件 2. 企业服务总线&#xff08;ESB&#xff09;模式3. Synchro ESB3. 微服务模式3.1 概述3.2 微服务架构模式方案3.2.1 聚合器微服务1&#xff09;概述2&#xff09;几种特殊的聚合微服务 3.2.2 …

RTT学习 cortex-m移植

Cortex-M移植 PRIMASK寄存器 PRIMASK寄存器为1位宽的中断屏蔽寄存器。在置位时&#xff0c;它会阻止不可屏蔽中断&#xff08;NMI&#xff09;和HardFault异常之外的所有异常&#xff08;包括中断&#xff09;。实际上&#xff0c;它是将当前异常优先级提升为0&#xff0c;这也…

Jenkins CI/CD 持续集成专题四 Jenkins服务器IP更换

一、查看brew 的 services brew services list 二、编辑 homebrew.mxcl.jenkins-lts.plist 将下面的httpListenAddress值修改为自己的ip 服务器&#xff0c;这里我是用的本机的ip 三 、重新启动 jenkins-lts brew services restart jenkins-lts 四 、浏览器访问 http://10.…

golang学习笔记(defer基础知识)

什么是defer defer语句用于golang程序中延迟函数的调用&#xff0c; 每次defer都会把一个函数压入栈中&#xff0c; 函数返回前再把延迟的函数取出并执行。 为了方便描述&#xff0c; 我们把创建defer的函数称为主函数&#xff0c; defer语句后面的函数称为延迟函数。延迟函数…

【Burpsuite靶场】XSS专题精讲

【个人】&#xff1a;NEUQ大一学生 【专业】&#xff1a;通信工程 (Communication Engineering) 【个人方向】&#xff1a;网安、开发双管齐下 【座右铭】&#xff1a;真正的英雄主义,就是看清生活的真相后依然热爱生活 -- 罗曼.罗兰 一、认识XSS&#xff08;跨站脚本攻击&…