记录-vue3+ts中websocket即时通讯小工具封装

921 阅读1分钟

基于之前写的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~~~~欢迎━(`∀´)ノ亻!随时留言和提问,欢迎交流和指正