Skip to content

前端——WebSocket实时通信在项目中的应用

Vue | July 11, 2026


文章链接:https://blog.csdn.net/qq_43201350/article/details/156124656

WebSocket提供了全双工通信能力,适用于实时性要求高的应用场景。本文将介绍WebSocket在前端项目中的实践。

1 WebSocket基础使用

// WebSocket客户端封装
class WebSocketClient {
  constructor(url, options = {}) {
    this.url = url
    this.options = {
      reconnectInterval: 1000,
      maxReconnectAttempts: 5,
      ...options
    }
    
    this.reconnectAttempts = 0
    this.listeners = {}
    this.connect()
  }
  
  connect() {
    this.ws = new WebSocket(this.url)
    
    this.ws.onopen = () => {
      console.log('WebSocket连接已建立')
      this.reconnectAttempts = 0
      this.emit('open')
    }
    
    this.ws.onmessage = (event) => {
      const data = JSON.parse(event.data)
      this.emit(data.type, data.payload)
    }
    
    this.ws.onclose = () => {
      console.log('WebSocket连接已关闭')
      this.handleReconnect()
      this.emit('close')
    }
    
    this.ws.onerror = (error) => {
      console.error('WebSocket错误:', error)
      this.emit('error', error)
    }
  }
  
  handleReconnect() {
    if (this.reconnectAttempts < this.options.maxReconnectAttempts) {
      this.reconnectAttempts++
      setTimeout(() => {
        console.log(`尝试重新连接 (${this.reconnectAttempts}/${this.options.maxReconnectAttempts})`)
        this.connect()
      }, this.options.reconnectInterval)
    }
  }
  
  send(type, payload) {
    if (this.ws.readyState === WebSocket.OPEN) {
      this.ws.send(JSON.stringify({ type, payload }))
    }
  }
  
  on(event, callback) {
    if (!this.listeners[event]) {
      this.listeners[event] = []
    }
    this.listeners[event].push(callback)
  }
  
  emit(event, data) {
    if (this.listeners[event]) {
      this.listeners[event].forEach(callback => callback(data))
    }
  }
}

2 实时消息组件

<template>
  <div class="chat-container">
    <div class="messages" ref="messagesContainer">
      <div 
        v-for="message in messages" 
        :key="message.id"
        :class="['message', message.type]"
      >
        <div class="message-content">{{ message.content }}</div>
        <div class="message-time">{{ formatTime(message.timestamp) }}</div>
      </div>
    </div>
    
    <div class="input-area">
      <input 
        v-model="inputMessage" 
        @keyup.enter="sendMessage"
        placeholder="输入消息..."
      >
      <button @click="sendMessage">发送</button>
    </div>
  </div>
</template>

<script setup>
import { ref, onMounted, nextTick } from 'vue'
import WebSocketClient from '@/utils/websocket'

const messages = ref([])
const inputMessage = ref('')
const messagesContainer = ref(null)
const wsClient = ref(null)

onMounted(() => {
  wsClient.value = new WebSocketClient('ws://localhost:8080/chat')
  
  wsClient.value.on('message', (data) => {
    messages.value.push({
      id: Date.now(),
      type: 'received',
      content: data.content,
      timestamp: new Date()
    })
    scrollToBottom()
  })
  
  wsClient.value.on('system', (data) => {
    messages.value.push({
      id: Date.now(),
      type: 'system',
      content: data.content,
      timestamp: new Date()
    })
    scrollToBottom()
  })
})

const sendMessage = () => {
  if (!inputMessage.value.trim()) return
  
  wsClient.value.send('message', {
    content: inputMessage.value
  })
  
  messages.value.push({
    id: Date.now(),
    type: 'sent',
    content: inputMessage.value,
    timestamp: new Date()
  })
  
  inputMessage.value = ''
  scrollToBottom()
}

const scrollToBottom = () => {
  nextTick(() => {
    if (messagesContainer.value) {
      messagesContainer.value.scrollTop = messagesContainer.value.scrollHeight
    }
  })
}

const formatTime = (timestamp) => {
  return timestamp.toLocaleTimeString()
}
</script>

3 心跳检测与断线重连

// 带心跳检测的WebSocket客户端
class HeartbeatWebSocket extends WebSocketClient {
  constructor(url, options = {}) {
    super(url, options)
    this.heartbeatInterval = options.heartbeatInterval || 30000
    this.heartbeatTimer = null
  }
  
  connect() {
    super.connect()
    
    // 启动心跳检测
    this.startHeartbeat()
  }
  
  startHeartbeat() {
    this.heartbeatTimer = setInterval(() => {
      this.send('ping', { timestamp: Date.now() })
    }, this.heartbeatInterval)
  }
  
  stopHeartbeat() {
    if (this.heartbeatTimer) {
      clearInterval(this.heartbeatTimer)
      this.heartbeatTimer = null
    }
  }
  
  handleReconnect() {
    this.stopHeartbeat()
    super.handleReconnect()
  }
  
  close() {
    this.stopHeartbeat()
    if (this.ws) {
      this.ws.close()
    }
  }
}