WebSocket 的封装

websocket 具体内容参考了 原文

import {ref, onUnmounted} from 'vue';
import dayjs from "dayjs";

class Socket {
    url;
    ws = null;
    opts;
    reconnectAttempts = 0;
    listeners = {};
    heartbeatInterval = null;

    constructor(url, opts = {}) {
        this.url = url;
        this.opts = {
            // 心跳检测
            heartbeatInterval: 30000,
            // 重新连接时间间隔
            reconnectInterval: 5000,
            // 重连的次数
            maxReconnectAttempts: 3,
            ...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) {
        if (event) {
            console.log(`%c websocket 已建立连接 ${dayjs(new Date()).format("HH:mm:ss")}`, 'color: green');
            this.reconnectAttempts = 0;
            this.startHeartbeat();
            this.emit('open', event);
        }

    }

    onMessage(event) {
        this.emit('message', event.data);
    }

    onError(event) {
        console.error('WebSocket error:', event);
        this.emit('error', event);
    }

    onClose(event) {
        console.log(`%c websocket 已断开连接 ${dayjs(new Date()).format("HH:mm:ss")}`, 'color: red');
        this.stopHeartbeat();
        this.emit('close', event);

        if (!window.wsIsClosed) {
            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) {
                console.log(`%c ❤ 检测 ${dayjs(new Date()).format("HH:mm:ss")}`, 'color: yellow');
                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) {
        if (this.listeners[event]) {
            delete this.listeners[event];
        }
    }

    emit(event, data) {
        this.listeners[event]?.forEach(callback => callback(data));
    }
}

export function useSocket(url, opts) {
    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)
    };
}

然后为了方便在 项目中进行使用,我们自定义一些 Hooks

import {useSocket} from "@/utils/websocket";
import {showNotify} from "@/utils/notifies";

export function useWebSocket(userId, store) {
    const startWS = () => {
        const {
            socket,
            on, off
        } = useSocket(`${process.env.VUE_APP_BASE_API.replace("https://", "wss://").replace("http://", "ws://")}/websocket/${userId}`);
        window.socketTarget = socket
        window.wsIsClosed = false;
        let messageQueue = []; // 用于存储消息的队列

        on('message', data => {
            if (data !== "ping") {
                const targetObjData = JSON.parse(data);
                if (targetObjData["msg_front_from"] && targetObjData) {
                    // 邮件消息
                    messageQueue.push(targetObjData); // 将消息添加到队列中
                } else {
                    // 通知消息
                    store.commit("message/setNewData", data);
                    store.dispatch("message/getUnReadMessageListApisDefault", {pageNo: 1, pageSize: 999})
                }
            }
        });

        // 在 3 秒后处理消息队列并触发 showNotify
        let timer = null;
        const processMessageQueue = () => {
            if (messageQueue.length > 0) {
                setTimeout(() => {
                    store.commit("message/setEmailData", messageQueue);
                    showNotify(messageQueue.length, messageQueue[0], () => {
                        localStorage.setItem('activeMenu', '/email/inbox')
                        router.push('/email/inbox')
                    });
                    messageQueue = []; // 清空消息队列
                    clearInterval(timer)
                }, 3000);
            }
        };
        timer = setInterval(processMessageQueue, 3000); // 每 3 秒处理一次消息队列
    };

    const stopWS = () => {
        if (window.socketTarget) {
            window.socketTarget.ws.close();
            window.wsIsClosed = true;
        }
    }

    return {
        startWS,
        stopWS,
    };
}

showNotify 的自定义

import {ElNotification} from "element-plus";

export function showNotify(messageNumber, item, fun) {
    if (item) {
        const {msg_front_from, msgTxt} = item
        const replaceEmail = msg_front_from.replace(/^<|>$/g, "")
        ElNotification({
            position: 'bottom-right',
            duration: 3000,
            dangerouslyUseHTMLString: true,
            onClick() {
                fun()
            },
            message: `<div class="notify_box">
            <div class="notify_left">
                <div class="notify_inner" style="background-color:#5272e5;padding: 0px 14px;border-radius: 8px">
                    <svg t="1708593116687" className="icon" viewBox="0 0 1024 1024" version="1.1"
                         xmlns="http://www.w3.org/2000/svg" p-id="5124" width="32" height="32">
                        <path
                            d="M891.904 145.92H128.512c-55.808 0-101.376 45.568-101.376 101.376v573.952c0 55.808 45.568 101.376 101.376 101.376h763.392c55.808 0 101.376-45.568 101.376-101.376V247.296c0-55.808-45.568-101.376-101.376-101.376z m-10.24 68.096l-352.768 285.184c-1.024 0.512-2.048 1.536-2.56 2.56-8.704 8.704-23.04 8.704-31.744 0l-2.56-2.56-352.768-285.184h742.4z m10.24 640.512H128.512c-18.432 0-32.768-14.848-32.768-32.768V266.752l351.744 284.672c17.408 16.896 39.936 25.088 62.464 25.088 22.528 0 45.056-8.192 62.464-25.088l351.744-284.672v554.496c0.512 18.432-13.824 33.28-32.256 33.28z"
                            p-id="5125" fill="#fff"></path>
                    </svg>
                </div>
                <div class="badge">
                    ${messageNumber}
                </div>
            </div>
            <div class="notify_right" style="width: 200px">
                <div>
                    <span class="userInfo">${replaceEmail}</span>
                </div>
                <div style="width: 185px;overflow: hidden;white-space: nowrap;text-overflow: ellipsis;">
                    ${msgTxt}
                </div>
            </div>
        </div>`
        })
    }
}

Hooks 的使用

import {useWebSocket} from "@/Hooks";
const {startWS, stopWS} = useWebSocket(userId, store);

//在合适的地方调用 startWS() 或 stopWS() 即可

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

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

相关文章

区块链 | 由外部实体导致的 NFT 安全问题

&#x1f98a;原文&#xff1a; Understanding Security Issues in the NFT Ecosystem &#x1f98a;警告&#xff1a; 本文只记录了原文的第 6 节。 1 问题描述 NFT 所指向的数字资产&#xff08;图片、视频等&#xff09;必须是可以访问的&#xff0c;这样 NFT 才具有意义…

iA Writer for Mac:简洁强大的写作软件

在追求高效写作的今天&#xff0c;iA Writer for Mac凭借其简洁而强大的功能&#xff0c;成为了许多作家、记者和学生的首选工具。这款专为Mac用户打造的写作软件&#xff0c;以其独特的设计理念和实用功能&#xff0c;助你轻松打造高质量的文章。 iA Writer for Mac v7.1.2中文…

数据挖掘之基于Lightgbm等多模型消融实验的信用欺诈检测实现

欢迎大家点赞、收藏、关注、评论啦 &#xff0c;由于篇幅有限&#xff0c;只展示了部分核心代码。 文章目录 一项目简介 二、功能三、系统四. 总结 一项目简介 一、项目背景 在当前的金融环境中&#xff0c;信用欺诈行为日益增多&#xff0c;给金融机构和消费者带来了巨大的损…

ThingsBoard PE专业版解决方案技术文档——温度湿度

1、项目总览 2、设备接入 3、设备告警 3.1 高温告警 创建一个Flag作为标杆&#xff0c;作为开启告警的开关。 3.2 低湿度告警 创建一个Flag作为标杆&#xff0c;作为开启告警的开关。 4、部件仪表 4.1 Entities table 部件预览&#xff1a; 标题样式&#xff1a; {"…

nuxt3项目服务端bulid后在本地浏览的3种方式(nuxi preview、Node.js Server、PM2)

你也许会问有了开发调试本地浏览&#xff0c;为什么还要服务端构建之后在本地浏览&#xff1f; 举个简单例子 在 Nuxt 3 服务端打包中&#xff0c;由于运行环境不同&#xff0c;无法直接访问 process 对象。服务端打包通常是在 Node.js 环境中进行的&#xff0c;而 process 对象…

Linux 手动部署JDK21 环境

1、下载包&#xff08;我下载的是tar) https://www.oracle.com/cn/java/technologies/downloads/#java21 完成后进行上传 2、检查已有JDK&#xff0c;并删除&#xff08;我原有是jdk8&#xff09; rpm -qa | grep -i java | xargs -n1 rpm -e --nodeps3、清理掉 profile中的j…

vue3 安装-使用之第一篇

首先需要node版本高于V16.14.1 安装 执行 npm create vitelatest 具体选择按照自己实际需要的来 Project name:项目名称 Select a framework:选择用哪种框架 &#xff08;我选择vue&#xff09; Select a variant: 选择用JS还是TS&#xff08;我选择JS&#xff09;找到项目&…

【云原生】Docker 实践(三):使用 Dockerfile 文件构建镜像

Docker 实践&#xff08;三&#xff09;&#xff1a;使用 Dockerfile 文件构建镜像 1.使用 Dockerfile 文件构建镜像2.Dockerfile 文件详解 1.使用 Dockerfile 文件构建镜像 Dockerfile 是一个文本文件&#xff0c;其中包含了一条条的指令&#xff0c;每一条指令都用于构建镜像…

笔记-PPT绘图导出高清无失真图片

问题描述&#xff1a;PPT绘图已经用了高清图&#xff08;jpg、tif格式&#xff09;&#xff0c;但论文图片还是不清晰&#xff0c;打印出来还是有点糊 以下是PPT导出高清不失真图片&#xff08;emf格式&#xff09;的具体描述。 目录 一、绘图工具二、操作步骤 一、绘图工具 …

Java | Leetcode Java题解之第60题排列序列

题目&#xff1a; 题解&#xff1a; class Solution {public String getPermutation(int n, int k) {int[] factorial new int[n];factorial[0] 1;for (int i 1; i < n; i) {factorial[i] factorial[i - 1] * i;}--k;StringBuffer ans new StringBuffer();int[] valid…

为什么公共事业机构会偏爱 TiDB :TiDB 数据库在某省妇幼健康管理系统的应用

本文介绍了某省妇幼健康管理系统的建设和数据库架构优化的过程。原有的数据库架构使用了 StarRocks 作为分析层&#xff0c;但随着业务的发展&#xff0c;这套架构暴露出诸多痛点&#xff0c;不再适应妇幼业务的需求。为解决这些问题&#xff0c;该系统选择了将原有架构中的 St…

Cesium 3dTileset 支持 uv 和 纹理贴图

原理: 使用自定义shader实现uv自动计算 贴图效果: uv效果:

AnyMP4 Blu-ray Ripper for Mac:您的蓝光影音转换专家

AnyMP4 Blu-ray Ripper for Mac&#xff0c;一款功能强大的蓝光影音转换软件&#xff0c;让您的蓝光内容焕发新生。 AnyMP4 Blu-ray Ripper for Macv9.0.58激活版下载 它采用最高效的解决方案&#xff0c;将蓝光光盘翻录为任何您想要的视频格式&#xff0c;无论是MP4、MKV还是A…

一个单例模式中使用std::unique_ptr引起的莫名其妙的COFF损坏的问题(未解决)

使用static std::unique_ptr和static std::shared_ptr都不行struct IElementAgendaEvents {//! Called to allow listeners to modify the agenda by adding/removing entries before applying tool operation. Return true if entries added or invalidated.virtual bool …

【webrtc】MessageHandler 6: 基于线程的消息处理:StunRequest实现包发送和超时重传

G:\CDN\rtcCli\m98\src\p2p\base\stun_request.cc使用OnMessage 实现包的发送和包的超时重传StunRequest 一个StunRequest 代表是一个独立的请求的发送STUN消息 要不是发送前构造好的,要不就是按照需要构建的使用StunRequestManager: 每一个STUNRequest 携带一个交互id 写入m…

windows11安装nginx

1.解压nginx安装包到没有中文的目录 2.双击运行nginx.exe 3.任务管理器查看是否有nginx进程 4.任务管理器->性能->资源监视器 5.网络->侦听端口&#xff0c;查看nginx侦听的端口&#xff0c;这里是90端口

nginx下载安装配置(含ssl)

下载安装环节 wget https://nginx.org/download/nginx-1.24.0.tar.gz tar -zxvf xxx.tar.gz yum -y install pcre-devel openssl openssl-devel ./configure --prefix/usr/local/nginx --with-http_stub_status_module --with-http_ssl_module --with-stream make & make i…

SpringCloud 学习笔记 —— 六、Ribbon:负载均衡(基于客户端)

SpringCloud 学习笔记 —— 一、背景-CSDN博客 SpringCloud 学习笔记 —— 二、微服务与微服务架构-CSDN博客 SpringCloud 学习笔记 —— 三、SpringCloud 入门概述-CSDN博客 SpringCloud 学习笔记 —— 四、SpringCloud Rest 学习环境搭建&#xff1a;服务提供者-CSDN博客 …

界面组件DevExpress中文教程 - 如何在Node.js应用中创建报表?

DevExpress Reporting是.NET Framework下功能完善的报表平台&#xff0c;它附带了易于使用的Visual Studio报表设计器和丰富的报表控件集&#xff0c;包括数据透视表、图表&#xff0c;因此您可以构建无与伦比、信息清晰的报表。 获取DevExpress Reporting最新正式版下载(Q技术…

Mybatis-Plus扩展接口InnerInterceptor

InnerInterceptor 接口就是 MyBatis-Plus 提供的一个拦截器接口&#xff0c;用于实现一些常用的 SQL 处理逻辑&#xff0c;处理 MyBatis-Plus 的特定功能,例如PaginationInnerInterceptor、OptimisticLockerInnerInterceptor 等,都实现了 InnerInterceptor 接口&#xff0c;并添…
最新文章