最近项目在做消息推送,所以就封装了个 websocket 帮助类,以此来减少重复代码。


ws.js 完整代码如下:

export default class WS {
    // 构造函数
    constructor(url, messageHandler, maxReconnectAttempts = 500) {
        this.url = url;
        this.ws = null;
        this.messageHandler = messageHandler;   // 外部传入的消息处理函数
        this.reconnectAttempts = 0;
        this.maxReconnectAttempts = maxReconnectAttempts; // 最大重连次数

        this.connect()
    }

    // 连接
    connect() {
        this.unBindEvent();
        this.ws = new WebSocket(this.url);
        this.bindEvent(); 
    }

    // 绑定事件
    bindEvent () {
        this.ws.onopen = () => {
            this.reconnectAttempts = 0;
            console.log('Websocket 连接成功');
        }

        this.ws.onmessage = (e) => {
            if (this.messageHandler && typeof this.messageHandler === 'function') {
                this.messageHandler(e.data);
            }
        }
        
        this.ws.onclose = () => {
            console.log('Websocket 连接断开,尝试重连...');
            if (this.reconnectAttempts < this.maxReconnectAttempts) {
                setTimeout(() => { this.connect() }, 3000);
                this.reconnectAttempts++;
            } else {
                console.error('达到最大重连次数,放弃连接');
            }
        }

        this.ws.onerror = (e) => {
            console.error('Websocket 连接错误', e);
        }
    }

    // 清空绑定的事件,避免内存泄漏
    unBindEvent () {
        if (this.ws) {
            this.ws.onopen = null;
            this.ws.onclose = null;
            this.ws.onerror = null;
            this.ws.onmessage = null;
            this.ws.close();
        }
    }

    // 发送消息
    sendMessage (message) {
        if (this.ws.readyState === WebSocket.OPEN) {
            this.ws.send(JSON.stringify(message)); // 数据的发送和接收都是字符串形式
        } else {
            console.error('连接未就绪');
        }
    }

    // 关闭,断开连接
    disconnect() {
        this.ws.close();
    }
}


在 VUE 中的使用示例

import WS from 'common/ws.js'

export default {
    created () {
        this.initSocket()
    }
    methods: {
        initSocket () {
            // 回调函数
            const handle = (data) => {
                try {
                    // 拿到后端推送过来的数据,并转为 JSON
                    const dt = JSON.parse(data);
                    
                    // do something...
                } catch (e) {
                    console.log('data', data);
                }
            }

            // 创建实例对象,进行连接
            // 并且通过连接地址传参(uid),传入回调函数(handle)
            const ws = new WS(`ws://此处填写您的IP地址或域名:端口号?uid=${this.user_id}`, handle);
        }
    }
}


关于 WebSocket 的一个知识点

问:WebSocket 重新连接,必须重新创建实例吗?可以用上一次创建的实例重连吗?

答:在 WebSocket 中,重新连接必须创建新的实例,无法直接复用上一次创建的实例。这是因为一旦 WebSocket 连接关闭(readyState 变为 CLOSED),该实例的底层连接已被销毁,无法再次打开。


WebSocket 实例的生命周期

WebSocket 对象的状态(readyState)分为

1- CONNECTING(0):连接尚未建立。

2- OPEN(1):连接已建立,可通信。

3- CLOSING(2):连接正在关闭。

4- CLOSED(3):连接已关闭或无法打开。

当连接关闭(CLOSED)后,实例的底层资源已被释放,无法通过任何 API 重新激活。此时调用 send() 会抛出错误,且无法重新建立连接。


实例绑定的连接不可复用

WebSocket 实例与底层 TCP 连接是绑定的。连接断开后,TCP 通道已失效,必须重新发起握手流程(即创建新实例)。

本文最后更新于 2025-03-04 09:53:44JAVASCRIPT
天生我材必有用,千金散尽还复来~~
作者:鄢云峰 YYF声明:转载请注明文章出处地址:https://yanyunfeng.com/article/75
评论
提交
Comments | 2 条评论
卡拉米2025-03-05 10:54:23
#1 回复
博主你好,第一次进入你的博客看见首页的动画非常震惊,能否告诉我如何实现的?非常感谢
鄢云峰站长2025-03-05 11:11:58
#2 回复
@卡拉米 看这篇文章 https://yanyunfeng.com/article/10 文章评论区有 DEMO 下载