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

45 阅读1分钟

websoket-vue3Hooks扩展版

<template>
  <div>
    <p>连接状态: {{ statusText }}</p>
    <p v-if="reconnectCount > 0">已尝试重连: {{ reconnectCount }} 次</p>
    <p v-if="isReachLimit" class="error">已达到最大重连次数,停止自动重连</p>
    <button v-if="isReachLimit" @click="resetRetry">手动重试</button>
  </div>
</template>

<script setup>
import { ref, reactive, computed, onMounted, onBeforeUnmount } from 'vue'

const ReadyState = {
  CONNECTING: 0,
  OPEN: 1,
  CLOSING: 2,
  CLOSED: 3
}

const useWebSocket = (url, options = {}) => {
  const config = reactive({
    reconnectInterval: 1000,
    maxReconnectInterval: 30000,
    reconnectDecay: 1.5,
    autoReconnect: true,
    maxRetries: 5,  // 默认改为有限次数
    onMaxRetries: null,  // 新增最大尝试回调
    ...options
  })

  // 响应式状态
  const wsInstance = ref(null)
  const reconnectCount = ref(0)
  const manualClose = ref(false)
  const status = ref(ReadyState.CLOSED)
  const isReachLimit = ref(false)

  // 计算属性
  const statusText = computed(() => {
    const texts = {
      [ReadyState.CONNECTING]: '连接中...',
      [ReadyState.OPEN]: '已连接',
      [ReadyState.CLOSING]: '关闭中',
      [ReadyState.CLOSED]: isReachLimit.value ? '已停止重连' : '已断开'
    }
    return texts[status.value] || '未知状态'
  })

  // 核心连接逻辑
  const connect = () => {
    if (isReachLimit.value) return
    
    manualClose.value = false
    wsInstance.value = new WebSocket(url)
    status.value = ReadyState.CONNECTING

    wsInstance.value.onopen = (event) => {
      reconnectCount.value = 0
      isReachLimit.value = false
      status.value = ReadyState.OPEN
      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()
      }
    }
    // 其他事件监听保持不变...
  }

  // 增强的重连调度
  const scheduleReconnect = () => {
    if (shouldStopReconnect()) {
      handleReconnectLimit()
      return
    }

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

  // 判断是否停止重连
  const shouldStopReconnect = () => {
    return (
      !config.autoReconnect ||
      manualClose.value ||
      reconnectCount.value >= config.maxRetries
    )
  }

  // 处理重连限制
  const handleReconnectLimit = () => {
    if (reconnectCount.value >= config.maxRetries) {
      isReachLimit.value = true
      config.onMaxRetries?.(reconnectCount.value)
    }
  }

  // 计算延迟时间(带随机抖动)
  const calculateDelay = () => {
    const baseDelay = Math.min(
      config.reconnectInterval * Math.pow(config.reconnectDecay, reconnectCount.value),
      config.maxReconnectInterval
    )
    return baseDelay + Math.random() * 500  // 添加随机抖动避免惊群效应
  }

  // 新增重置方法
  const resetRetry = () => {
    reconnectCount.value = 0
    isReachLimit.value = false
    connect()
  }

  // 暴露给组件的API
  return {
    send: (data) => { /* 保持不变 */ },
    close: () => { /* 保持不变 */ },
    resetRetry,
    status,
    statusText,
    reconnectCount,
    isReachLimit
  }
}

// 使用示例
const { 
  resetRetry,
  isReachLimit,
  statusText,
  reconnectCount 
} = useWebSocket('wss://echo.websocket.org', {
  onOpen: (event) => {
    console.log('连接成功', event)
  },
  onMaxRetries: (count) => {
    console.warn(`已达到最大重连次数 ${count} 次`)
    // 可以在这里触发UI通知
  },
  maxRetries: 3  // 设置自定义重试次数
})
</script>

<style>
.error {
  color: #ff4444;
  font-weight: bold;
}
</style>