- A+
所属分类:Web前端
把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
的主要原因