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

  • ai问答:vue3+pinia+WebSocket 封装断线重连(实战)已关闭评论
  • 140 次浏览
  • A+
所属分类:Web前端
摘要

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


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;     } });  // 心跳连接 function heartbeat() {     socket.send("heartbeat"); }  // 十分钟一次 (简陋心跳,也请忽略吧^_^) heartbeat(); setInterval(heartbeat, 1000 * 60 * 10); 

状态管理

# alarm.ts 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)

# home.vue 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(() => {     // 列表数据 -- 根据参数 {"cmd":"1"} 实现数据交互     $socket.send({cmd: 1});      // 返回的数据格式     const type1 = {         type: 1,         message: [             {id: 1, name: '', value: ''},             {id: 2, name: '', value: ''},         ],     }; });  const click = ()=>{     // 其他数据 -- 点击列表的某一项,根据参数 获取数据     $socket.send({cmd: 2,extras:{id:1}});      // 返回的数据格式     const type2 = {         type: 2,         message: {             id: 1, name: '', value: ''         },     }; }  // 第一种 监听方式: 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的主要原因