美文网首页
vue3 websocket插件编写,支持断开重连

vue3 websocket插件编写,支持断开重连

作者: 嘻哈章鱼小丸子 | 来源:发表于2023-11-23 17:30 被阅读0次
    背景

    由于业务需要,项目需要全局实例化一个websocket组件供全局调用,在连接成功后向后端发送token进行鉴权,再向后端发送需要监听的数据,后端按照前端发送的这些参数回推变动的数据给前端。

    调研and实现

    调研了些插件,发现都不太符合业务需要,于是自己写了一个简单的。但是实际使用的时候发现会报如下的错:


    nginx超时断开

    原来是:使用了nginx 代理,在长时间没有连接的情况下会断开,这样前端就收不到后端的推送。

    解决办法

    一、增加nginx 的超时时间:设置 proxy_read_timeout 尽量大一点,按业务需求将这个时间尽量设置的长一点。
    二、前端需要定时主动判断连接状态来确认连接是否断开,断开的话就重连,这样也能解决因其他意外情况导致的连接断开问题。

    终极优化代码

    思路是:在不修改后端代码(不需要后端推送pong之类的数据 )的前提下,通过定时判断连接状态来实现。

    经过验证,如果nginx 超时,会执行onclose 方法进行重连,前端也能知道websocket状态是断开的。

    终极版代码如下:

    import { ref } from 'vue'
    const WebSocketPlugin = {
        install(app) {
            let ws = ref(null)
            let message = ref(null)
            let callback = ref(null)
            const wsUrl = ref(null)
            //避免ws重复连接
            const lockReconnect = ref(false)
            const connect = url => {
                wsUrl.value = url
                try {
                    if ('WebSocket' in window) {
                        ws.value = new WebSocket(url)
                        ws.value.onopen = () => {
                            //心跳检测重置
                            heartCheck.reset().start()
                            ws.value.send(
                                JSON.stringify({
                                    token: localStorage.getItem('token')
                                })
                            )
                            sendAndMessage()
                        }
                        ws.value.onmessage = event => {
                            //拿到任何消息都说明当前连接是正常的
                            heartCheck.reset().start()
                            callback.value(JSON.parse(event.data))
                        }
    
                        ws.value.onclose = () => {
                            ws.value === null
                            reconnect(url)
                        }
                        ws.value.onerror = () => {
                            ws.value = null
                            reconnect(url)
                        }
                    }
                } catch (e) {
                    reconnect(url)
                }
            }
    
            const reconnect = url => {
                if (lockReconnect.value) {
                    return
                }
                lockReconnect.value = true
                setTimeout(function () {
                    connect(url)
                    lockReconnect.value = false
                }, 2000)
            }
            // 关闭
            const close = () => {
                if (ws.value && ws.value.readyState !== WebSocket.CLOSED) {
                    ws.value = null
                    message.value = null
                    callback.value = null
                }
            }
    
            //发送消息初始化
            const sendAndMessage = ()=> {
                if (ws.value?.readyState === WebSocket.OPEN && message.value) {
                    ws.value.send(
                        JSON.stringify({
                            tables: message.value
                        })
                    )
                } else  if (ws.value?.readyState !== WebSocket.CONNECTING) {
                    reconnect(wsUrl.value)
                }
            }
            //页面初始化方法
            const init = (msg, cb) => {
                message.value = msg
                callback.value = cb
                sendAndMessage()
            }
    
            //心跳检测
            const heartCheck = {
                //这里设置10分钟发一次心跳
                timeout: 10 * 60 * 1000,
                timeoutObj: null,
                reset: function () {
                    clearTimeout(this.timeoutObj)
                    return this
                },
                start: function () {
                    this.timeoutObj && clearTimeout(this.timeoutObj)
                    this.timeoutObj = setTimeout(function () {    
                        //检测状态           
                        sendAndMessage()
                    }, this.timeout)
                }
            }
    
            app.provide('wsInit', init)
            app.provide('wsClose', close)
            app.provide('wsConnect', connect)
        }
    }
    export default WebSocketPlugin
    

    在main.js引入该插件:

    import WebSocketPlugin from './plugins/websocket.js'
    app.use(WebSocketPlugin)
    

    在首页调用连接和关闭方法:

    const wsConnect = inject('wsConnect')
    const wsClose = inject('wsClose')
    wsConnect(window.wsUrl + '/v1/system/watch')
    
    //退出登录关闭连接
    onUnmounted(() => {
        wsClose()
    })
    

    在具体详情页发送监听数据:

    const wsInit = inject('wsInit')
    wsInit(['XXX'], () => {
        //收到推送后执行的回调函数
    })
    

    相关文章

      网友评论

          本文标题:vue3 websocket插件编写,支持断开重连

          本文链接:https://www.haomeiwen.com/subject/zkyvwdtx.html