ai问答:vue3+pinia+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(() => {
// 列表数据 -- 通过给`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
的主要原因
热门相关:仙城纪 最强装逼打脸系统 无限杀路 名门天后:重生国民千金 学霸女神超给力