进入页面,生命周期挂载后,window监听ws连接online
正常情况,心跳包检测避免断开
非正常情况,ws.onclose断开,
判断1000状态吗,触发重连函数。
定时器,重连,判断5次,每次增加30s,最后重新继续循环重连。
手动自主断开,正常断开,则不触发重连函数,直接ws.close断开
离开页面,ws.close断开,移除window监听ws连接online
<template>
<!-- 大屏容器 -->
<div class="dashboard-container">
<!-- 动态连接状态指示器,根据connectionStatus改变样式 -->
<div class="connection-status" :class="connectionStatus">
{{ statusText }}
</div>
<!-- 数据卡片容器,使用自适应网格布局 -->
<div class="dashboard">
<!-- 用户数卡片 -->
<DataCard
title="实时用户数"
:value="dashboardData.userCount"
icon="👥"
color="#4CAF50"
/>
<!-- 销售额卡片,使用自定义货币格式化 -->
<DataCard
title="今日销售额"
:value="dashboardData.sales"
icon="💰"
color="#2196F3"
prefix="¥"
:formatter="formatCurrency"
/>
<!-- 订单卡片 -->
<DataCard
title="订单数量"
:value="dashboardData.orders"
icon="📦"
color="#9C27B0"
/>
</div>
</div>
</template>
<script setup>
/* 核心逻辑模块 */
import { ref, reactive, onMounted, onBeforeUnmount } from 'vue'
import DataCard from './components/DataCard.vue'
// 响应式数据存储(大屏核心数据)
const dashboardData = reactive({
userCount: 0, // 实时用户数
sales: 0, // 销售额(单位:元)
orders: 0 // 订单数量
})
/* WebSocket连接管理 */
const connectionStatus = ref('connecting') // 连接状态:connecting/connected/disconnected
const statusText = ref('连接中...') // 状态显示文本
const reconnectAttempts = ref(0) // 当前重连尝试次数
const maxReconnectAttempts = 5 // 最大重连次数
const baseReconnectDelay = 1000 // 基础重连延迟(毫秒)
let ws = null // WebSocket实例引用
let heartbeatTimer = null // 心跳定时器
let reconnectTimer = null // 重连定时器
/**
* 货币格式化方法
* @param {number} value - 原始数值
* @returns {string} 格式化后的货币字符串
*/
const formatCurrency = (value) => {
return Number(value).toLocaleString('zh-CN', {
style: 'currency',
currency: 'CNY',
minimumFractionDigits: 2,
maximumFractionDigits: 2
})
}
/* WebSocket连接管理模块 */
/**
* 初始化WebSocket连接
* 1. 创建WebSocket实例
* 2. 绑定事件处理器
*/
const initWebSocket = () => {
// 创建WebSocket连接(生产环境应使用wss协议)
ws = new WebSocket('wss://api.example.com/realtime-data')
// 连接成功回调
ws.onopen = () => {
console.log('WebSocket connected')
connectionStatus.value = 'connected' // 更新连接状态
statusText.value = '已连接'
reconnectAttempts.value = 0 // 重置重连计数器
startHeartbeat() // 启动心跳检测
}
// 消息接收处理
ws.onmessage = (event) => {
try {
const data = JSON.parse(event.data)
// 合并数据更新(触发响应式更新)
Object.assign(dashboardData, {
userCount: data.userCount,
sales: data.sales,
orders: data.orders
})
} catch (error) {
console.error('数据解析失败:', error)
}
}
// 连接关闭处理
ws.onclose = (event) => {
console.log('连接关闭:', event.code, event.reason)
stopHeartbeat() // 停止心跳
// 非正常关闭时触发重连
if (!event.wasClean) {
handleReconnect()
}
}
// 错误处理
ws.onerror = (error) => {
console.error('WebSocket 错误:', error)
connectionStatus.value = 'disconnected'
statusText.value = '连接异常'
ws.close() // 确保关闭连接
}
}
/* 心跳机制模块 */
/**
* 启动心跳检测
* 每30秒发送一次ping消息保持连接
*/
const startHeartbeat = () => {
heartbeatTimer = setInterval(() => {
// 仅当连接处于打开状态时发送心跳
if (ws?.readyState === WebSocket.OPEN) {
ws.send(JSON.stringify({ type: 'ping' }))
}
}, 30000)
}
/**
* 停止心跳检测
*/
const stopHeartbeat = () => {
if (heartbeatTimer) {
clearInterval(heartbeatTimer)
heartbeatTimer = null
}
}
/* 自动重连模块 */
/**
* 自动重连机制(指数退避算法)
* 1. 计算延迟时间:delay = base * 2^attempts
* 2. 最大延迟不超过30秒
* 3. 达到最大重试次数后停止
*/
const handleReconnect = () => {
if (reconnectAttempts.value >= maxReconnectAttempts) {
statusText.value = '连接失败,请刷新页面'
return
}
connectionStatus.value = 'connecting'
statusText.value = `正在重连 (${reconnectAttempts.value + 1}/${maxReconnectAttempts})`
// 计算指数退避延迟
const delay = Math.min(
baseReconnectDelay * Math.pow(2, reconnectAttempts.value),
30000 // 最大延迟30秒
)
// 设置重连定时器
reconnectTimer = setTimeout(() => {
reconnectAttempts.value++
initWebSocket() // 重新初始化连接
}, delay)
}
/* 资源清理模块 */
/**
* 清理所有活动资源
* 1. 关闭WebSocket连接
* 2. 清除所有定时器
*/
const cleanup = () => {
if (ws) {
ws.close()
ws = null
}
stopHeartbeat()
if (reconnectTimer) {
clearTimeout(reconnectTimer)
}
}
/* 生命周期管理 */
onMounted(() => {
// 初始化WebSocket连接
initWebSocket()
// 监听网络恢复事件
window.addEventListener('online', () => {
// 当连接不存在或已关闭时重新连接
if (!ws || ws.readyState === WebSocket.CLOSED) {
initWebSocket()
}
})
})
// 组件卸载前清理资源
onBeforeUnmount(() => {
cleanup()
})
</script>
<style scoped>
/* 容器样式 */
.dashboard-container {
padding: 20px;
background: #000;
min-height: 100vh;
color: white;
}
/* 自适应网格布局 */
.dashboard {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 20px;
max-width: 1920px;
margin: 0 auto;
}
/* 连接状态指示器样式 */
.connection-status {
position: fixed;
top: 20px;
right: 20px;
padding: 8px 20px;
border-radius: 20px;
background: #ff4444;
transition: all 0.3s;
}
/* 连接成功状态 */
.connection-status.connected {
background: #00C851;
}
/* 连接中状态(带呼吸动画) */
.connection-status.connecting {
background: #ffbb33;
animation: pulse 1.5s infinite;
}
@keyframes pulse {
0% { opacity: 1; }
50% { opacity: 0.5; }
100% { opacity: 1; }
}
</style>