【useWebsocket】vue3+vite封装websocket hooks

不废话了,直接上干货。可直接食用
ts刚开始用,不合理的地方欢迎指导,虚心请教

import { onUnmounted, ref } from 'vue'

import { useSystemStore } from '@/store/modules/systemStore/systemStore'

interface QueryType {
  url: string //url
  options?: OptionsType //配置
  params?: object //url参数
  onmessage?: Function //回调函数
}
interface OptionsType {
  maxReconnect?: number //最大重连次数, -1 标识无限重连
  interval?: number //心跳间隔
  timeout?: number //  响应超时时间
  pingMessage?: string // 心跳请求信息
}
const defaultOptions = {
  maxReconnect: 6,
  interval: 30 * 1000,
  timeout: 10 * 1000,
  pingMessage: 'ping'
}

/**
 * websocket封装
 * @param {object} param0
 * @returns  serverMessage, sendMessage, destroySocket
 */
export default function useWebSocket({ url, options = {}, params, onmessage }: QueryType) {
  const OPTION = { ...defaultOptions, ...options }
  let socket: WebSocket | null = null //websocket实例
  let pingTimeoutObj: NodeJS.Timeout | null = null //延时发送心跳的定时器
  let pongTimeoutObj: NodeJS.Timeout | null = null //接收心跳响应的定时器
  let reconnectCount: number = 0 // 重连尝试次数
  let lockReconnect: Boolean = false // 重连锁,避免多次重连

  const token = useSystemStore().getToken

  // 初始化连接socket
  function initWebSocket() {
    const VITE_APP_SOCKET_API = import.meta.env.VITE_APP_SOCKET_API
    const _params = objectToEncodeQuery(params)
    const wsUri = `${VITE_APP_SOCKET_API}${url}?token=${token}` + _params

    socket = new WebSocket(wsUri)

    // 连接成功
    socket.onopen = onOpen
    // 连接错误
    socket.onerror = onError
    // 接收信息
    socket.onmessage = onMessage
    // 连接关闭
    socket.onclose = onClose
  }

  /**
   * 连接成功事件
   */
  function onOpen() {
    // 开启心跳
    startHeartbeat()
    reconnectCount = 0
  }

  /**
   * 连接失败事件
   * @param e
   */
  function onError(e) {
    console.error(`WebSocket connection error:${e.code} ${e.reason} ${e.wasClean}`)
    reconnect()
  }

  /**
   * 连接关闭事件
   * @param e
   */
  function onClose() {
    reconnect()
  }

  /**
   * 重新连接
   */
  function reconnect() {
    if (!token) {
      return
    }

    if (lockReconnect || (OPTION.maxReconnect !== -1 && reconnectCount > OPTION.maxReconnect)) {
      return
    }

    lockReconnect = true
    setTimeout(() => {
      reconnectCount++
      // 建立新连接
      initWebSocket()
      lockReconnect = false
    }, 5000)
  }

  /**
   * 清空定时器
   */
  function clearTimeoutObj() {
    pingTimeoutObj && clearTimeout(pingTimeoutObj)
    pongTimeoutObj && clearTimeout(pongTimeoutObj)
  }

  /**
   * 开启心跳
   */
  function startHeartbeat() {
    console.log('startHeartbeat')
    // 清空定时器
    pingTimeoutObj && clearTimeout(pingTimeoutObj)
    // 延时发送下一次心跳
    pingTimeoutObj = setTimeout(() => {
      sendMessage(OPTION.pingMessage)
    }, OPTION.interval)
  }

  const serverMessage = ref()
  /**
   * 接收服务器推送的信息
   * @param msgEvent
   */
  function onMessage(msgEvent) {
    console.log('msgEvent')
    // 收到服务器信息,心跳重置并发送
    startHeartbeat()
    const text = msgEvent.data

    if (text === 'pong') {
      return
    }
    const data = JSON.parse(msgEvent.data)
    serverMessage.value = data.data
    onmessage && onmessage(data.data)
  }

  function sendMessage(message: string) {
    // 如果连接正常
    if (socket && socket.readyState === 1) {
      // 这里发送一个心跳,后端收到后,返回一个心跳消息,
      socket.send(message)
      // 心跳发送后,如果服务器超时未响应则断开,如果响应了会被重置心跳定时器
      pongTimeoutObj && clearTimeout(pongTimeoutObj)
      pongTimeoutObj = setTimeout(() => {
        socket && socket.close()
      }, OPTION.timeout)
    } else {
      // 否则重连
      reconnect()
    }
  }
  /**
   * 销毁socket
   */
  function destroySocket() {
    if (socket) {
      lockReconnect = true
      socket.close()
      clearTimeoutObj()
    }
  }
  //  断开连接
  onUnmounted(() => {
    destroySocket()
  })

  /**
   * 对象转url参数
   */
  function objectToEncodeQuery(query: object | undefined): string {
    if (!query) return ''

    const params = Object.keys(query)
      .filter(key => query[key])
      .map(key => {
        return encodeURIComponent(key) + '=' + encodeURIComponent(query[key])
      })
      .join('&')

    return params
  }

  initWebSocket()

  return { serverMessage, sendMessage, destroySocket }
}

  • 4
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
好的,我理解你的问题是关于在 Vue 3 和 Vite 环境下封装 WebSocket 和心跳机制的方法。下面是一个示例代码,供你参考: ```javascript import { ref, onMounted, onUnmounted } from 'vue' export default function useWebSocket(url) { const message = ref('') let socket = null const connectWebSocket = () => { socket = new WebSocket(url) socket.onopen = () => { console.log('WebSocket connected') socket.send('Hello, WebSocket') } socket.onmessage = event => { message.value = event.data } socket.onclose = () => { console.log('WebSocket disconnected') setTimeout(() => { connectWebSocket() }, 5000) } } const startHeartbeat = () => { setInterval(() => { if (socket.readyState === WebSocket.OPEN) { socket.send('Heartbeat') } }, 3000) } onMounted(() => { connectWebSocket() startHeartbeat() }) onUnmounted(() => { socket.close() }) return { message } } ``` 上面的代码中,我们定义了一个名为 `useWebSocket` 的自定义 Hook,它接收一个 URL 参数,用于指定 WebSocket 服务器的地址。在 `useWebSocket` 函数内部,我们使用 `ref` 创建了一个响应式的 `message` 变量,用于存储 WebSocket 收到的消息。然后,我们声明了一个 `socket` 变量,用于存储 WebSocket 连接对象。 接着,我们定义了 `connectWebSocket` 函数,它用于创建 WebSocket 连接,并在连接打开、收到消息、连接关闭时分别执行对应的操作。当连接关闭时,我们使用 `setTimeout` 函数在 5 秒后重新连接 WebSocket。 然后,我们定义了 `startHeartbeat` 函数,它用于定时发送心跳包。在 `onMounted` 生命周期钩子中,我们调用了 `connectWebSocket` 和 `startHeartbeat` 函数。在 `onUnmounted` 生命周期钩子中,我们关闭了 WebSocket 连接。最后,我们返回 `message` 变量,以便在组件中使用。 使用这个自定义 Hook 的示例代码如下: ```javascript <template> <div>{{ message }}</div> </template> <script> import useWebSocket from './useWebSocket' export default { setup() { const { message } = useWebSocket('wss://example.com/ws') return { message } } } </script> ``` 在组件的 `setup` 函数中,我们调用了 `useWebSocket` 自定义 Hook,将返回的 `message` 变量绑定到模板中的 `<div>` 元素上。这样,我们就完成了在 Vue 3 和 Vite 环境下封装 WebSocket 和心跳机制的操作。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值