美文网首页
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