Vue3+vite中使用stompjs
背景
使用sockjs-client+stompjs完成一个socket连接客户端很简单,但是这个库是8年前的,至今无人维护了。
且其中也有不少的问题,例如:断线重连问题,订阅维护问题,重复订阅问题,vue2狮山代码中订阅消息发送错乱问题,以及经常出现莫名的无法连接socket问题The connection has not been established yet
其中最重要的是长时间置于后台,socket会断开
以至于我们需要写大量的代码去解决这些问题
// 经常会遇到websocket的状态为open 但是stompClient的状态却是未连接状态,故此处需要把连接断掉 然后重连
stompClient.connect(
{}, // 此处注意更换自己的用户名,最好以参数形式带入
(frame) => {
console.log('🆗连接成功!', frame)
this._stompClient = stompClient
this.isConnected = true
if (this.listenerList && this.listenerList.length > 0) {
this.listenerList.forEach((item) => {
const sub = this._stompClient.subscribe(item.topic, item.callback, item.params)
item.sub = sub
})
}
resolve(frame)
},
(err) => {
// 第一次连接失败和连接后断开连接都会调用这个函数 此处调用重连
// console.log('连接stomp失败:', err)
this._stompClient && this._stompClient.disconnect()
this._stompClient = null
this.websocket && this.websocket.close()
this.websocket = null
this.isConnected = false
if (location.hash.includes('login')) {
return
}
const initTho = useThrottleFn(() => {
this.initSocket()
}, 5000)
initTho()
resolve(err)
},
现状
我采用了@stomp/stompjs这个库,本着不打扰社区的理念,放弃了自己之前开发的websocket连接库,转为使用这个
它使用起来也很简单 最大的亮点莫过于支持二进制传输
import { Client } from '@stomp/stompjs'
let stompUrl = 'ws://' + location.hostname + ':' + location.port + import.meta.env.VITE_STOMP_API
if (import.meta.env.MODE === 'development' || import.meta.env.MODE === 'test') {
stompUrl = import.meta.env.VITE_STOMP_API
}
const useStompJsStore = defineStore('StompJs', {
state: () => ({
_client: null,
// 重连后重新订阅
listenerSubs: [],
}),
getters: {
stompClient: (state) => state._client,
},
actions: {
// 激活stomp客户端
setActivate() {
this._client = new Client({
brokerURL: stompUrl,
connectHeaders: {},
debug: function (str) {
console.log('🌐 ~ file: stompjs.js:19 ~ setActivate ~ str:', str)
},
reconnectDelay: 5000,
heartbeatIncoming: 1000,
heartbeatOutgoing: 1000,
})
this._client.onConnect = (frame) => {
console.log('🌐 ~ file: frame.js:19 ~ setActivate ~ str:', frame)
if (frame.indexOf('CONNECTED') >= 0) {
// 重连后取listenerSubs中存在的订阅重新连接,避免在每个订阅中判断是否为CONNECTED状态
}
// Do something, all subscribes must be done is this callback
// This is needed because this will be executed after a (re)connect
}
this._client.onStompError = function (frame) {
// Will be invoked in case of error encountered at Broker
// Bad login/passcode typically will cause an error
// Complaint brokers will set `message` header with a brief message. Body may contain details.
// Compliant brokers will terminate the connection after any error
console.log('🐛Broker reported error: ' + frame.headers['message'])
console.log('🐛Additional details: ' + frame.body)
}
this._client.activate()
},
/**
*
* @param topic 订阅路径
* @param callback 订阅回调
* @param headers 请求参数
* @param isRemoveDup 是否需要移除重复订阅
*/
// 目前还未尝试两个相同订阅,是否会产生前一个订阅数据无法获得的问题
sendSubscribe(topic, callback, headers = {}, isRemoveDup = true) {
const subs = this._client.subscribe(topic, callback, headers)
// 此处需要管理重复的订阅
if (isRemoveDup) {
this.listenerSubs.push({
topic,
subs,
})
console.log('------', subs)
}
},
// 断开stomp客户端
async setDeActivate() {
await this._client.deactivate()
},
},
})
export default useStompJsStore