Vue3 中 WebSocket 的全局封装

Gary Chen
Vue3 中 WebSocket 的全局封装

Vue3 中 WebSocket 的全局封装

在 Vue3 中封装 WebSocket 可以实现全局的通信管理,便于在各个组件中使用。下面是一个完整的封装方案:

1. 创建 WebSocket 服务文件

首先创建一个 websocket.service.js 文件:

// src/services/websocket.service.js

class WebSocketService {
  constructor() {
    this.socket = null
    this.messageListeners = []
    this.reconnectAttempts = 0
    this.maxReconnectAttempts = 5
    this.reconnectInterval = 3000 // 3秒
  }

  connect(url) {
    if (this.socket) {
      this.disconnect()
    }

    this.socket = new WebSocket(url)

    this.socket.onopen = () => {
      console.log('WebSocket connected')
      this.reconnectAttempts = 0
    }

    this.socket.onmessage = (event) => {
      try {
        const data = JSON.parse(event.data)
        this.notifyListeners(data)
      } catch (error) {
        console.error('Error parsing WebSocket message:', error)
      }
    }

    this.socket.onclose = (event) => {
      console.log('WebSocket disconnected:', event.code, event.reason)
      this.handleReconnect(url)
    }

    this.socket.onerror = (error) => {
      console.error('WebSocket error:', error)
    }
  }

  disconnect() {
    if (this.socket) {
      this.socket.close()
      this.socket = null
    }
  }

  handleReconnect(url) {
    if (this.reconnectAttempts < this.maxReconnectAttempts) {
      this.reconnectAttempts++
      console.log(`Attempting to reconnect (${this.reconnectAttempts}/${this.maxReconnectAttempts})...`)
      setTimeout(() => this.connect(url), this.reconnectInterval)
    } else {
      console.log('Max reconnection attempts reached')
    }
  }

  addMessageListener(callback) {
    this.messageListeners.push(callback)
    return () => {
      this.messageListeners = this.messageListeners.filter(listener => listener !== callback)
    }
  }

  notifyListeners(data) {
    this.messageListeners.forEach(listener => listener(data))
  }

  sendMessage(message) {
    if (this.socket && this.socket.readyState === WebSocket.OPEN) {
      const messageString = typeof message === 'string' ? message : JSON.stringify(message)
      this.socket.send(messageString)
    } else {
      console.error('WebSocket is not connected')
    }
  }
}

// 创建单例实例
const webSocketService = new WebSocketService()

export default webSocketService

2. 在 Vue 应用中全局提供 WebSocket 服务

main.jsmain.ts 中:

import { createApp } from 'vue'
import App from './App.vue'
import webSocketService from './services/websocket.service'

const app = createApp(App)

// 全局提供 WebSocket 服务
app.provide('websocket', webSocketService)

// 连接到 WebSocket 服务器
webSocketService.connect('ws://your-websocket-server.com')

app.mount('#app')

3. 在组件中使用 WebSocket

选项式 API 使用方式

<script>
export default {
  inject: ['websocket'],
  data() {
    return {
      messages: []
    }
  },
  created() {
    // 添加消息监听器
    this.unsubscribe = this.websocket.addMessageListener(this.handleMessage)
  },
  beforeUnmount() {
    // 移除消息监听器
    if (this.unsubscribe) {
      this.unsubscribe()
    }
  },
  methods: {
    handleMessage(data) {
      this.messages.push(data)
      console.log('Received message:', data)
    },
    sendMessage() {
      const message = { text: 'Hello from Vue!', timestamp: new Date().toISOString() }
      this.websocket.sendMessage(message)
    }
  }
}
</script>

组合式 API 使用方式

<script setup>
import { inject, onBeforeUnmount, ref } from 'vue'

const websocket = inject('websocket')
const messages = ref([])

const handleMessage = (data) => {
  messages.value.push(data)
  console.log('Received message:', data)
}

// 添加消息监听器
const unsubscribe = websocket.addMessageListener(handleMessage)

// 组件卸载前移除监听器
onBeforeUnmount(() => {
  unsubscribe()
})

const sendMessage = () => {
  const message = { text: 'Hello from Vue!', timestamp: new Date().toISOString() }
  websocket.sendMessage(message)
}
</script>

4. 高级功能扩展

类型支持 (TypeScript)

如果你使用 TypeScript,可以添加类型定义:

// src/types/websocket.d.ts
declare module 'websocket' {
  interface WebSocketMessage {
    type: string
    payload: any
  }

  type MessageListener = (message: WebSocketMessage) => void

  export interface WebSocketService {
    connect(url: string): void
    disconnect(): void
    sendMessage(message: WebSocketMessage | string): void
    addMessageListener(callback: MessageListener): () => void
  }
}

连接状态管理

可以在服务中添加连接状态跟踪:

class WebSocketService {
  constructor() {
    // ...其他代码
    this.connectionState = 'disconnected' // 'connecting', 'connected', 'disconnected', 'error'
  }

  // 在 connect 方法中更新状态
  connect(url) {
    this.connectionState = 'connecting'
    // ...其他代码
  }

  // 在 onopen 中更新状态
  this.socket.onopen = () => {
    this.connectionState = 'connected'
    // ...其他代码
  }

  // 在 onclose 和 onerror 中更新状态
  // ...

  getConnectionState() {
    return this.connectionState
  }
}

自动重连策略

上面的示例已经包含了基本的重连逻辑,你可以根据需要扩展更复杂的重连策略,比如指数退避算法。

5. 使用建议

  1. 连接时机:通常在应用初始化时连接,但也可以根据需要延迟连接
  2. 错误处理:确保有良好的错误处理和重连机制
  3. 性能考虑:避免在消息监听器中执行耗时操作
  4. 安全性:对于敏感数据,考虑添加加密或认证机制

这种封装方式提供了全局的 WebSocket 管理,可以在应用的任何组件中轻松使用,同时保持了良好的可维护性和扩展性。