实时通信
vue3-element-admin 内置了 WebSocket 支持,基于 STOMP 协议实现实时通信能力。
STOMP 协议
STOMP (Simple Text Oriented Messaging Protocol) 是一个简单的文本消息传递协议,提供了可互操作的连接格式,允许客户端与任意 STOMP 消息代理进行交互。
核心概念
- 连接(Connect):建立 WebSocket 连接
- 订阅(Subscribe):订阅消息主题
- 发送(Send):向服务器发送消息
- 断开(Disconnect):断开连接
useStomp
useStomp 是封装的 STOMP 客户端组合函数:
typescript
import { useStomp } from '@/composables'
const {
connect, // 连接
disconnect, // 断开
subscribe, // 订阅
unsubscribe, // 取消订阅
send, // 发送消息
isConnected // 连接状态
} = useStomp()基本用法
typescript
import { useStomp } from '@/composables'
const { connect, subscribe, send, disconnect } = useStomp()
// 1. 建立连接
onMounted(() => {
connect()
})
// 2. 订阅主题
const subscription = subscribe('/topic/messages', (message) => {
console.log('收到消息:', message.body)
})
// 3. 发送消息
send('/app/message', { text: 'Hello World' })
// 4. 断开连接
onUnmounted(() => {
disconnect()
})配置选项
typescript
const { connect } = useStomp({
// WebSocket 端点
brokerURL: import.meta.env.VITE_APP_WS_ENDPOINT,
// 连接成功回调
onConnect: () => {
console.log('WebSocket 连接成功')
},
// 连接断开回调
onDisconnect: () => {
console.log('WebSocket 连接断开')
},
// 错误回调
onStompError: (frame) => {
console.error('STOMP 错误:', frame)
},
// 心跳配置
heartbeatIncoming: 10000,
heartbeatOutgoing: 10000,
// 自动重连
reconnectDelay: 5000
})应用场景
1. 字典同步
使用 useDictSync 实现字典数据实时同步:
typescript
import { useDictSync } from '@/composables'
const { startSync, stopSync, onDictChange } = useDictSync()
// 开始同步
startSync()
// 监听字典变更
onDictChange((code, action) => {
console.log(`字典 ${code} 发生 ${action} 操作`)
})
// 停止同步
onUnmounted(() => {
stopSync()
})2. 在线人数统计
使用 useOnlineCount 实现在线人数实时统计:
typescript
import { useOnlineCount } from '@/composables'
const { onlineCount, startCount, stopCount } = useOnlineCount()
// 开始统计
startCount()
// 显示在线人数
console.log(`当前在线人数:${onlineCount.value}`)
// 停止统计
onUnmounted(() => {
stopCount()
})3. 实时通知
typescript
import { useStomp } from '@/composables'
const { connect, subscribe } = useStomp()
// 连接并订阅通知
connect()
// 订阅用户私有通知
subscribe('/user/queue/notifications', (message) => {
const notification = JSON.parse(message.body)
ElNotification({
title: notification.title,
message: notification.content,
type: notification.type
})
})
// 订阅广播通知
subscribe('/topic/broadcast', (message) => {
const broadcast = JSON.parse(message.body)
ElMessage.info(broadcast.content)
})4. 聊天功能
typescript
import { useStomp } from '@/composables'
const { connect, subscribe, send } = useStomp()
const messages = ref<Message[]>([])
// 连接
connect()
// 订阅聊天消息
subscribe('/topic/chat', (message) => {
const chatMessage = JSON.parse(message.body)
messages.value.push(chatMessage)
})
// 发送消息
function sendMessage(content: string) {
send('/app/chat', {
content,
sender: currentUser.value.username,
timestamp: Date.now()
})
}环境配置
开发环境
bash
# .env.development
VITE_APP_WS_ENDPOINT=ws://localhost:8080/ws生产环境
bash
# .env.production
VITE_APP_WS_ENDPOINT=wss://api.example.com/wsVite 代理配置
typescript
// vite.config.ts
export default defineConfig({
server: {
proxy: {
'/ws': {
target: 'ws://localhost:8080',
ws: true,
changeOrigin: true
}
}
}
})Nginx 配置
nginx
# WebSocket 代理
location /ws {
proxy_pass http://backend:8080/ws;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_read_timeout 3600s;
}后端配置
Spring Boot 配置
java
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
// 启用简单消息代理
config.enableSimpleBroker("/topic", "/queue");
// 应用程序目标前缀
config.setApplicationDestinationPrefixes("/app");
// 用户目标前缀
config.setUserDestinationPrefix("/user");
}
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/ws")
.setAllowedOriginPatterns("*")
.withSockJS();
}
}发送消息示例
java
@Service
public class NotificationService {
@Autowired
private SimpMessagingTemplate messagingTemplate;
// 发送广播消息
public void broadcast(String message) {
messagingTemplate.convertAndSend("/topic/broadcast", message);
}
// 发送给指定用户
public void sendToUser(String username, String message) {
messagingTemplate.convertAndSendToUser(
username,
"/queue/notifications",
message
);
}
}最佳实践
1. 连接管理
在应用级别管理 WebSocket 连接,避免重复连接:
typescript
// App.vue 或 Layout 组件
import { setupWebSocket, cleanupWebSocket } from '@/composables'
onMounted(() => {
setupWebSocket()
})
onUnmounted(() => {
cleanupWebSocket()
})2. 错误处理
typescript
const { connect } = useStomp({
onStompError: (frame) => {
console.error('STOMP 错误:', frame.headers.message)
ElMessage.error('WebSocket 连接异常')
},
onWebSocketError: (event) => {
console.error('WebSocket 错误:', event)
}
})3. 重连机制
typescript
const { connect } = useStomp({
reconnectDelay: 5000, // 5秒后重连
onDisconnect: () => {
console.log('连接断开,将在5秒后重连...')
}
})4. 心跳检测
typescript
const { connect } = useStomp({
heartbeatIncoming: 10000, // 接收心跳间隔
heartbeatOutgoing: 10000 // 发送心跳间隔
})