基于之前写的websokcet通讯vue2版本小工具不能愉快的在vue3划水了,因为是基于vue2版本进行的改造,主要是ts方面的变化,总体改造变更不大。
本次改造任务原则:
1、开箱即用,使用者所写代码尽量简洁
2、支持单页面/多页面多个socket实例同时存在
🎈代码部分
utils/socket.ts
/*
@Author: yjt
@Date: 2023-11-14 17:14:36
* @LastEditors: yjt
* @LastEditTime: 2024-03-13 10:00:24
* @FilePath: \**\src\utils\socket.ts
@Description:
Websocket 支持多实例化已挂载到vue实例上
import { inject } from "vue"
const socket: any = inject("$socket")
onMounted(() => {
socket.initWebSocket(`${import.meta.env.VITE_APP_SOCKET_URL}${userStore.user.user.userId}`, getDataBenData) // data泵socket)
})
onBeforeUnmount(() => {
socket.closeWebSocket(`${import.meta.env.VITE_APP_SOCKET_URL}${userStore.user.user.userId}`) //关闭指定websocket连接
sokcet.closeAllWebSockets() //关闭所有websocket实例,一般不用
})
*/
interface WebSocketEntry {
socket: WebSocket
callback: Function
isConnect: boolean
heartbeatTime: number
reConnectNum?: number
url: string
}
class WebSocketService {
private webSockets: Record<string, WebSocketEntry> = {} // 存储WebSocket实例
private heartbeatTimers: Record<string, NodeJS.Timeout> = {} // 存储心跳定时器
/**
初始化websocket
@param {*} url socket地址
@param {*} callback 回调函数
*/
public initWebSocket(url: string, callback: Function): void {
console.log("初始化websocket: ", url)
const websocketUrl = url?.replace("http://", "ws://")
if (callback && typeof callback !== "function") {
throw new Error("callback is not a function")
}
if ("WebSocket" in window) {
const socket = new WebSocket(websocketUrl)
socket.onopen = () => this.webSocketOpen(socket)
socket.onmessage = (event) => this.webSocketOnMessage(event, socket)
socket.onclose = (event) => this.webSocketOnClose(event, socket)
socket.onerror = (error) => this.webSocketOnError(error, socket)
const entry: WebSocketEntry = {
socket,
callback,
isConnect: true, // socket连接状态
heartbeatTime: 1 * 60 * 1000, // 心跳监测时间
url: websocketUrl,
}
this.webSockets[url] = entry
} else {
ElMessage.error("该浏览器不支持 WebSocket!")
}
}
private webSocketOpen = (socket: WebSocket): void => {
console.log("WebSocket连接成功:", socket)
const entry = this.findEntryBySocket(socket)
if (entry) {
this.startHeartbeat(entry.url, socket, entry.heartbeatTime)
entry.reConnectNum = 0
}
}
private webSocketOnMessage = (event: MessageEvent, socket: WebSocket): void => {
const entry = this.findEntryBySocket(socket)
if (entry) {
const { callback } = entry
const data = JSON.parse(event.data)
callback({ res: data, isConnect: entry.isConnect })
}
}
private webSocketOnClose = (event: CloseEvent, socket: WebSocket): void => {
const entry = this.findEntryBySocket(socket)
if (entry) {
this.stopHeartbeat(entry.url)
entry.isConnect = false
const { callback } = entry
callback({ isConnect: entry.isConnect })
if (event.code === 1006) {
if (entry.reConnectNum < 3) {
this.initWebSocket(entry.url, entry.callback)
entry.reConnectNum++
} else {
console.error("WebSocket连接不上,请刷新页面或联系开发人员!")
}
}
}
}
private webSocketOnError = (error: Event, socket: WebSocket): void => {
const entry = this.findEntryBySocket(socket)
if (entry) {
this.stopHeartbeat(entry.url)
entry.isConnect = false
const { callback } = entry
callback({ isConnect: entry.isConnect })
console.error("WebSocket连接发生错误:", socket, error)
}
}
private webSocketSend = (socket: WebSocket, data: any): void => {
if (socket.readyState === WebSocket.OPEN) {
socket.send(JSON.stringify(data))
} else {
console.error("WebSocket连接未打开:", socket)
}
}
private startHeartbeat = (url: string, socket: WebSocket, heartbeatTime: number): void => {
this.heartbeatTimers[url] = setInterval(() => {
// this.webSocketSend(socket, { heartbeat: true })
this.webSocketSend(socket, { cmd: "keeplive" })
}, heartbeatTime)
}
private stopHeartbeat = (url: string): void => {
clearInterval(this.heartbeatTimers[url])
}
private findEntryBySocket = (socket: WebSocket): any => {
const entries = Object.values(this.webSockets)
return entries.find((entry) => entry.socket === socket)
}
public closeWebSocket = (url: string): void => {
console.log("WebSocket连接关闭")
const entry = this.webSockets[url]
if (entry) {
const { socket } = entry
socket.close()
delete this.webSockets[url] // 从存储中移除WebSocket实例
this.stopHeartbeat(url)
entry.reConnectNum = 0
}
}
public closeAllWebSockets = (): void => {
console.log("关闭所有WebSocket连接")
Object.values(this.webSockets).forEach((entry) => {
const { socket } = entry
socket.close()
})
this.webSockets = {} // 清空存储的WebSocket实例
}
public sendMessage = (url: string, data: any): void => {
const entry = this.webSockets[url]
if (entry) {
this.webSocketSend(entry.socket, data)
} else {
console.error("WebSocket实例不存在:", url)
}
}
}
export default new WebSocketService()
main.ts
import webSocketService from "@/utils/socket"
// 全局方法挂载
app.provide("$socket", webSocketService)
layout/index.vue
<template>
<div>test</div>
</template>
<script setup lang="ts">
import { inject } from "vue"
const socket: any = inject("$socket")
onMounted(() => {
socket.initWebSocket(`${import.meta.env.VITE_APP_SOCKET_URL}${userStore.user.user.userId}`, getDataBenData) // data泵socket)
})
onBeforeUnmount(() => {
socket.closeWebSocket(`${import.meta.env.VITE_APP_SOCKET_URL}${userStore.user.user.userId}`)
})
</script>
最后
以上是基于vue+ts版本的websocket 拷贝即用 小工具,vue3选择性较多,hook、pinia/store都可以实现websocket的实例统一管理和封装,诸位可按需选择
如果觉得对您有所帮助可以给个关注和star~~~~欢迎━(`∀´)ノ亻!随时留言和提问,欢迎交流和指正