把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);
狀態管理
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
的主要原因