当前位置: 首页 > news >正文

前端封装WebSocket工具n

  1. Web API 提供的 WebSocket 类,封装一个 Socket 类
// socket.js
import modal from '@/plugins/modal'
const baseURL = import.meta.env.VITE_APP_BASE_WS;
const EventTypes = ['open', 'close', 'message', 'error', 'reconnect'];
const DEFAULT_CHECK_TIME = 55 * 1000; // 心跳检测的默认时间
const DEFAULT_CHECK_COUNT = 3; // 心跳检测默认失败重连次数
const DEFAULT_CHECK_DATA = { Type: 1, Parameters: ['alive'] }; // 心跳检测的默认参数 - 跟后端协商的
const CLOSE_ABNORMAL = 1006; // WebSocket非正常关闭code码class EventMap {deps = new Map();depend(eventType, callback) {this.deps.set(eventType, callback);}notify(eventType, event) {if (this.deps.has(eventType)) {this.deps.get(eventType)(event);}}
}class Socket extends WebSocket {heartCheckData = DEFAULT_CHECK_DATA;heartCheckTimeout = DEFAULT_CHECK_TIME;heartCheckInterval = null;heartCheckCount = DEFAULT_CHECK_COUNTconstructor(options, dep, reconnectCount = 0) {let _baseURL = baseURLconst { url, protocols, query = {}, greet = null, customBase = null } = options;const _queryParams = Object.keys(query).reduce((str, key) => {if (typeof query[key] !== 'object' && typeof query[key] !== 'function') {return str += str.length > 0 ? `&${key}=${query[key]}` : `${key}=${query[key]}`;} else {return str;}}, '');if (customBase) {_baseURL = customBase}super(`${_baseURL}${url}?${_queryParams}`, protocols);this._currentOptions = options;this._dep = dep;this._reconnectCount = reconnectCount;greet && Object.assign(this, {heartCheckData: greet})this.initSocket();}// 初始化WebSocketinitSocket() {// 监听webSocket的事件this.onopen = function (e) {this._dep.notify('open', e);this.heartCheckStart();}this.onclose = function (e) {this._dep.notify('close', e);// 如果WebSocket是非正常关闭 则进行重连if (e.code === CLOSE_ABNORMAL) {if (this._reconnectCount < this.heartCheckCount) {this._reconnectCount++;const _socket = new Socket(this._currentOptions, this._dep, this._reconnectCount);this._dep.notify('reconnect', _socket);} else {return modal.msgError('WebSocket重连失败, 请联系技术客服!');}}}this.onerror = function (e) {this._dep.notify('error', e);}this.onmessage = function (e) {// 如果后端返回的是二进制数据if (e.data instanceof Blob) {const reader = new FileReader()reader.readAsArrayBuffer(e.data)reader.onload = (ev) => {if (ev.target.readyState === FileReader.DONE) {this._dep.notify('message', ev.target?.result);}}} else {// 处理普通数据try {const _parseData = JSON.parse(e.data);this._dep.notify('message', _parseData);} catch (error) {console.log(error)}}}}// 订阅事件subscribe(eventType, callback) {if (typeof callback !== 'function') throw new Error('The second param is must be a function');if (!EventTypes.includes(eventType)) throw new Error('The first param is not supported');this._dep.depend(eventType, callback);}// 发送消息sendMessage(data, options = {}) {const { transformJSON = true } = options;let result = data;if (transformJSON) {result = JSON.stringify(data);}this.send(result);}// 关闭WebSocketcloseSocket(code, reason) {this.close(code, reason);}// 开始心跳检测heartCheckStart() {this.heartCheckInterval = setInterval(() => {if (this.readyState === this.OPEN) {let transformJSON = typeof this.heartCheckData === 'object'this.sendMessage(this.heartCheckData, { transformJSON });} else {this.clearHeartCheck();}}, this.heartCheckTimeout)}// 清除心跳检测clearHeartCheck() {clearInterval(this.heartCheckInterval);}// 重置心跳检测resetHeartCheck() {clearInterval(this.heartCheckInterval);this.heartCheckStart();}
}
// 默认的配置项
const defaultOptions = {url: '',protocols: '',query: {},
}export const useSocket = (options = defaultOptions) => {if (!window.WebSocket) return modal.msgWarning('您的浏览器不支持WebSocket, 请更换浏览器!');const dep = new EventMap();const reconnectCount = 0;return new Socket(options, dep, reconnectCount);
}
  1. 使用这个封装好的 useSocket 函数,以在 Vue3中使用为例
// xx.jsx or xx.vue
import { useSocket } from './socket.js'
const socket = ref(null) // WebSocket实例
const initWebSocket = () => {const options = {url: '/<your url>',query: {// something params},}socket.value = useSocket(options)socket.value.subscribe('open', () => {console.log('WebSocket连接成功!')const greet = 'hello'// 发送打招呼消息socket.value.sendMessage(greet)})socket.value.subscribe('close', reason => {console.log('WebSocket连接关闭!', reason)})socket.value.subscribe('message', result => {console.log('WebSocket接收到消息:', result)})socket.value.subscribe('error', err => {console.log('WebSocket捕获错误:', err)})socket.value.subscribe('reconnect', _socket => {console.log('WebSocket断开重连:', _socket)socket.value = _socket})
}
initWebSocket()
  1. debug 我们的心跳检测是否有效
// 测试心跳检测重连 手动模拟断开的情况
if (this._reconnectCount > 0) return;
const tempTimer = setInterval(() => {this.close();if (this._reconnectCount < 3) {console.log('重连');this._reconnectCount++;const _socket = new Socket(this._currentOptions, this._dep, this._reconnectCount);this._dep.notify('reconnect', _socket);} else {return clearInterval(tempTimer);}
}, 3 * 1000)

相关文章:

  • 【XR】MR芯片 和 VR芯片之争
  • 函数调用及Chain——SQL+GLM
  • Java 中的 设计模式详解
  • Linux容器大师:K8s集群部署入门指南
  • 【开源工具】Python打造智能IP监控系统:邮件告警+可视化界面+配置持久化
  • 扣子智能体3:进行音乐创作
  • Axure疑难杂症:中继器制作下拉菜单(多级中继器高级交互)
  • 【Dockerfile】Dockerfile打包Tomcat及TongWeb应用镜像(工作实践踩坑教学)
  • 阿里云服务迁移实战: 07-其他服务迁移
  • 【IP101】图像处理基础:从零开始学习颜色操作(RGB、灰度化、二值化、HSV变换)
  • 详细说明C++ 中的左值、右值与移动语义
  • nginx 配置要领
  • Spring Boot 数据库最佳实践:从自动配置到高性能优化
  • 2025东三省D题深圳杯D题数学建模挑战赛数模思路代码文章教学
  • LeetCode167_两数之和 Ⅱ - 输入有序数组
  • 大连理工大学选修课——机器学习笔记(6):决策树
  • 通过IP计算分析归属地
  • 2025年“深圳杯”数学建模挑战赛A题-芯片热弹性物理参数估计
  • 硬盘分区丢失≠末日!3步逻辑恢复法+物理修复全流程图解
  • 网易爆米花 1.8.8 | 免费无广告,支持多网盘聚合和智能刮削技术,提供顶级画质和逼真音效的影视管理应用
  • 应急管理部派出工作组赴山西太原小区爆炸现场指导救援处置
  • 神十九乘组安全顺利出舱
  • 不准打小孩:童年逆境经历视角下的生育友好社会
  • 浦发银行一季度净利175.98亿增1.02%,不良率微降
  • 中国人保不再设监事会,国寿集团未再设置监事长职务
  • 中国人保聘任田耕为副总裁,此前为工行浙江省分行行长