ai问答:vue3+pinia+WebSocket 封装断线重连(实战)

发布时间 2023-05-08 10:12:17作者: 让梦想纵横

socket实例 挂载到全局

为方便梳理,请忽略typescript

# main.ts
import {createApp} from 'vue'

import App from './App.vue'
import {socket} from "@/xihu/socket"
import router from "@/xihu/router"

const app = createApp(App);
app.use(router).mount('#root');

// 全局挂载
app.config.globalProperties.$socket = socket;

Socket封装(断线重连)

这个WebSocket类封装了WebSocket的连接、重连、发送数据等方法。
connect方法中,它会连接WebSocket,并绑定相关事件监听。
onclose事件中,它会调用reconnect方法进行重连。
reconnect方法会在一定时间内重连,并且重连的时间间隔会越来越长,最大重连次数达到设定值后就不再重连。
这样就实现了一个可以断线重连的WebSocket连接。
我们可以在vue应用中使用这个类来进行WebSocket通信,并处理可能出现的网络断开重连情况。

# socket.ts

// @ts-nocheck
export default class Socket {
    constructor(url, protocols) {
        this.url = url
        this.protocols = protocols
        this.ws = null
        this.reconnectTimeout = 1000
        this.maxReconnectTimes = 5
    }

    connect() {
        this.ws = new WebSocket(this.url, this.protocols)
        this.ws.onopen = () => {
            console.log('WebSocket连接成功')
            this.reconnectTimes = 0
        }
        this.ws.onclose = () => {
            console.log('WebSocket断开连接')
            this.reconnect()
        }
        this.ws.onerror = err => {
            console.log('WebSocket连接出错', err)
        }
    }

    reconnect() {
        if (this.reconnectTimes < this.maxReconnectTimes) {
            setTimeout(() => {
                this.connect()
                this.reconnectTimes++
            }, this.reconnectTimeout)
            this.reconnectTimeout *= 2
        } else {
            console.log('WebSocket重连超过最大次数,放弃重连')
        }
    }

    // 消息发送
    msg(param) {
        if (param === 'heartbeat') {
            this.ws.send(param);
        } else {
            this.ws.send(JSON.stringify(param));
        }
    }

    // 延迟发送
    timeout(param) {
        setTimeout(() => {
            this.msg(param);
        }, 2000)
    }

    send(param) {
        if (this.ws.readyState === this.ws.OPEN) {
            this.msg(param);
        } else if (this.ws.readyState === this.ws.CONNECTING) {
            this.timeout(param);
        } else {
            this.timeout(param);
        }
    }
}

实例化socket

通过type关键字,分发数据,并且通过pinia(vuex)存储实时数据
在消息回调函数,处理返回的数据,使用type关键字对应各种推送事件,比如:实时设备告警、地图显示用户坐标等等...

// @ts-nocheck
import {createPinia} from 'pinia';
import {useAlarm} from '@/store/alarm';

// 状态管理
export const pinia = createPinia();
export const store = useAlarm(pinia);

export function wsInit(callback) {
    const url = 'ws://api.xx.cn';
    const init = new Socket(url);

    // 连接 WebSocket
    init.connect();

    // 监听 WebSocket
    init.ws.onmessage = function (ev) {
        if (ev && ev.data && ev.data.indexOf('subscribe') > -1) {
            console.log('subscribe->', ev.data);
        } else if (ev && ev.data) {
            var data = eval('(' + ev.data + ')');
            callback(data);
        }
    };

    return init;
}

// 消息回调
export const socket = wsInit((data) => {
    switch (data.type) {
        case 1:
            store.setType1(data);
            break;
        case 2:
            store.setType2(data.message);
            break;
    }
});

状态管理

import {defineStore} from 'pinia'
export const useAlarm = defineStore('user', {
    state:()=>({
        type1:{},
        type2:{},
    }),
    getters:{
        getType1: (state) => state.type1,
        getType2: (state) => state.type2,
    },
    actions:{
        setType1(payload: any) {
            this.type1 = payload;
        },
        setType2(payload: any) {
            this.type2 = payload;
        },
    },
})

在页面中,使用数据(pinia)

import { watch, computed, onMounted, getCurrentInstance} from 'vue'
import {useAlarm} from "@/xihu/store/alarm";
const store = useAlarm();

// 还记得全局挂载的`$socket`吧,这样使用
const ctx: any = getCurrentInstance();
const {$socket} = ctx.appContext.config.globalProperties;

onMounted(() => {
    // 列表数据 -- 通过给`websocket`发送`{"cmd":"1"}`实现数据交互
    $socket.send({cmd: 1});
});

const click = ()=>{
    // 其他数据 -- 点击列表的某一项,根据参数获取数据
    $socket.send({cmd: 2,extras:{id:123}});
}

// 第一种 监听方式:
const type1 = computed(() => store.type1);
watch(type1, (message: any) => {
    console.log('/computed/watch/', message);
}, {deep: true});

// 第二种 监听方式:
store.$subscribe(({events}: any, state) => {
    if (events.key === 'type1') {
        console.log('/$subscribe/', state.type1);
    }
});

大多数情况,数据是后台主动推送的,比如:告警数据,这也是使用websocket的主要原因