千载后,百篇存,更无一字不清真。

55 阅读1分钟

websoket-Vue3Hooks封装

import { ref, reactive, onMounted, onBeforeUnmount } from 'vue'

  


// WebSocket状态枚举

const ReadyState = {

  CONNECTING: 0,

  OPEN: 1,

  CLOSING: 2,

  CLOSED: 3

}

  


// ws://61.163.60.12:19100/im/chat/736fc042-41ab-4f99-8edc-f18a2ebb52f6

const useWebSocket = (url: any, options = {}) => {

  


  // 配置合并

  const config: any = reactive({

    reconnectInterval: 1000,

    maxReconnectInterval: 30000, // 最大重连间隔时间

    reconnectDecay: 1.5, // 重连指数衰减 每次间隔翻倍

    autoReconnect: true,

    maxRetries: Infinity,

    ...options

  })

  console.log(config);

  
  


  // 响应式状态

  const wsInstance: any = ref(null)

  const reconnectCount = ref(0)

  const manualClose = ref(false)

  const status = ref(ReadyState.CLOSED)

  const statusText = ref('未连接')

  


  // 状态映射文本

  const statusMap = {

    [ReadyState.CONNECTING]: '连接中...',

    [ReadyState.OPEN]: '已连接',

    [ReadyState.CLOSING]: '关闭中',

    [ReadyState.CLOSED]: '已断开'

  }

  


  // 初始化连接

  const connect = () => {

    manualClose.value = false

    wsInstance.value = new WebSocket(url)

    console.log('wsInstance', wsInstance.value);

 
    status.value = ReadyState.CONNECTING

  
  


    // 更新状态文本
    const updateStatusText = () => {
      statusText.value = statusMap[status.value] || '未知状态'
    }

    updateStatusText()

  


    // 事件监听
    wsInstance.value.onopen = (event: any) => {

      reconnectCount.value = 0
      status.value = ReadyState.OPEN
      updateStatusText()
      config.onOpen?.(event)

    }


    wsInstance.value.onmessage = (event: any) => {
      config.onMessage?.(event.data)
    }


    wsInstance.value.onclose = (event: any) => {
      status.value = ReadyState.CLOSED
      updateStatusText()
      config.onClose?.(event)
      if (!manualClose.value && config.autoReconnect) {
        scheduleReconnect()
      }
    }

    wsInstance.value.onerror = (error: any) => {
      status.value = ReadyState.CLOSED
      updateStatusText()
      config.onError?.(error)
      wsInstance.value?.close()
    }
  }


  // 计划重连
  const scheduleReconnect = () => {
    if (reconnectCount.value >= config.maxRetries) return

  
    const delay = Math.min(
      config.reconnectInterval * Math.pow(config.reconnectDecay, reconnectCount.value),
      config.maxReconnectInterval
    )

    setTimeout(() => {
      reconnectCount.value++
      connect()
    }, delay)
  }

 

  // 发送消息

  const send = (data: any) => {
    if (wsInstance.value?.readyState === WebSocket.OPEN) {
      wsInstance.value.send(data)
    } else {
      console.error('消息发送失败: WebSocket未连接')
    }
  }

  // 手动关闭
  const close = () => {
    manualClose.value = true
    wsInstance.value?.close()
  }


  // 组件挂载时自动连接
  onMounted(() => {
    // 自动绑定创建组件生命周期
    connect()
  })


  /* // 组件卸载时关闭连接
  onBeforeUnmount(() => {
    console.log('onBeforeUnmountclose',wsInstance.value);
    close()
  }) */


  return {
    send,
    close,
    status,
    statusText,
    reconnectCount,
    connect
  }

}

export default useWebSocket;

  


// 在组件中使用

/*

let heartbeatTimer:any;

const {
  send,
  close,
  statusText,
  reconnectCount
} = useWebSocket('wss://echo.websocket.org', {
  maxRetries: 5, // 最多尝试5次重连  (扩展)
  onOpen: (event) => {
    console.log('连接成功', event)
    heartbeatTimer = setInterval(() => {  (扩展)
      send('ping') // 发送心跳包
    }, 30000)
    send('Hello from Vue3!')
  },
  onMessage: (data) => {
    console.log('收到消息:', data)
  },
  onClose: (event) => {
    console.log('连接关闭', event)
    clearInterval(heartbeatTimer) (扩展)
    if (reconnectCount.value >= 5) {'服务器连接失败,请刷新页面重试'}  (扩展)
  },
  onError: (error) => {
    console.error('连接错误:', error)
  },
  reconnectInterval: 1000,
  maxReconnectInterval: 10000

}) */