vue3/vite中使用stompjs

2,863 阅读2分钟

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() => ({
    _clientnull,
    // 重连后重新订阅
    listenerSubs: [],
  }),
  getters: {
    stompClient(state) => state._client,
  },
  actions: {
    // 激活stomp客户端
    setActivate() {
      this._client = new Client({
        brokerURL: stompUrl,
        connectHeaders: {},
        debugfunction (str) {
          console.log('🌐 ~ file: stompjs.js:19 ~ setActivate ~ str:', str)
        },
        reconnectDelay5000,
        heartbeatIncoming1000,
        heartbeatOutgoing1000,
      })

      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