JavaScript 实现代码
这段代码包含 WebSocket 连接管理、JSON 解析和 UI 渲染逻辑:
// WebSocket 连接管理
let socket;
const messageContainer = document.getElementById('message-container');
const socketStatus = document.getElementById('socket-status');
const connectBtn = document.getElementById('connect-btn');
const lastUpdate = document.getElementById('last-update');
// 连接 WebSocket
function connectWebSocket() {
// 关闭现有连接(如果存在)
if (socket && socket.readyState !== WebSocket.CLOSED) {
socket.close();
}
// 创建新连接
socket = new WebSocket('ws://localhost:8080/websocket');
// 更新 UI 状态
socketStatus.innerHTML = '<i class="fa fa-circle-o-notch fa-spin mr-2"></i>连接中...';
socketStatus.classList.remove('disconnected', 'connected');
connectBtn.disabled = true;
connectBtn.innerHTML = '<i class="fa fa-circle-o-notch fa-spin mr-2"></i>连接中...';
// 监听连接成功
socket.onopen = () => {
messageContainer.innerHTML = '';
socketStatus.innerHTML = '<i class="fa fa-check-circle mr-2"></i>已连接';
socketStatus.classList.add('connected');
connectBtn.innerHTML = '<i class="fa fa-plug mr-2"></i>断开连接';
connectBtn.disabled = false;
connectBtn.classList.remove('bg-primary');
connectBtn.classList.add('bg-red-500', 'hover:bg-red-600');
appendSystemMessage('WebSocket 连接已建立');
};
// 监听消息接收
socket.onmessage = (event) => {
try {
// 解析 JSON 数据
const jsonData = JSON.parse(event.data);
// 更新最后更新时间
const now = new Date();
lastUpdate.textContent = now.toLocaleString();
// 格式化显示数据
const messageDiv = document.createElement('div');
messageDiv.className = 'message-card';
messageDiv.style.opacity = '0';
messageDiv.style.transform = 'translateY(10px)';
messageDiv.style.transition = 'opacity 0.3s ease, transform 0.3s ease';
// 动态生成内容
let contentHtml = '';
for (const [key, value] of Object.entries(jsonData)) {
if (typeof value === 'object' && value !== null) {
// 嵌套对象处理
contentHtml += `<p><strong>${formatKey(key)}:</strong></p>`;
contentHtml += '<div class="ml-4 bg-gray-50 p-3 rounded my-2">';
for (const [nestedKey, nestedValue] of Object.entries(value)) {
contentHtml += `<p class="text-sm"><strong>${formatKey(nestedKey)}:</strong> ${formatValue(nestedValue)}</p>`;
}
contentHtml += '</div>';
} else {
// 普通字段处理
contentHtml += `<p><strong>${formatKey(key)}:</strong> ${formatValue(value)}</p>`;
}
}
messageDiv.innerHTML = contentHtml;
messageContainer.prepend(messageDiv);
// 添加动画效果
setTimeout(() => {
messageDiv.style.opacity = '1';
messageDiv.style.transform = 'translateY(0)';
}, 10);
} catch (error) {
console.error('解析 JSON 失败:', error);
appendError(`解析消息失败: ${error.message}`);
}
};
// 监听连接关闭
socket.onclose = (event) => {
socketStatus.innerHTML = '<i class="fa fa-times-circle mr-2"></i>已断开';
socketStatus.classList.remove('connected');
socketStatus.classList.add('disconnected');
connectBtn.innerHTML = '<i class="fa fa-plug mr-2"></i>连接';
connectBtn.disabled = false;
connectBtn.classList.remove('bg-red-500', 'hover:bg-red-600');
connectBtn.classList.add('bg-primary');
appendSystemMessage(`WebSocket 连接已关闭 (代码: ${event.code})`);
};
// 监听错误
socket.onerror = (error) => {
console.error('WebSocket 错误:', error);
appendError(`WebSocket 错误: ${error.message}`);
};
}
// 格式化键名(将 camelCase 转为空格分隔的标题格式)
function formatKey(key) {
return key
.replace(/([A-Z])/g, ' $1')
.replace(/^./, (str) => str.toUpperCase());
}
// 格式化值(处理特殊类型)
function formatValue(value) {
if (typeof value === 'number' && !isNaN(value)) {
return value;
} else if (typeof value === 'boolean') {
return value ? '<span class="text-green-600">是</span>' : '<span class="text-red-600">否</span>';
} else if (value === null || value === undefined) {
return '<span class="text-gray-400">null</span>';
} else if (typeof value === 'string') {
// 尝试解析为日期
const date = new Date(value);
if (!isNaN(date.getTime())) {
return date.toLocaleString();
}
return value;
}
return JSON.stringify(value);
}
// 添加系统消息
function appendSystemMessage(message) {
const systemMessage = document.createElement('div');
systemMessage.className = 'text-center text-gray-500 italic py-2';
systemMessage.innerHTML = `<i class="fa fa-info-circle mr-1"></i> ${message}`;
messageContainer.prepend(systemMessage);
}
// 添加错误消息
function appendError(message) {
const errorDiv = document.createElement('div');
errorDiv.className = 'error-card';
errorDiv.innerHTML = `<p><i class="fa fa-exclamation-triangle mr-1"></i> ${message}</p>`;
messageContainer.prepend(errorDiv);
}
// 连接按钮事件
connectBtn.addEventListener('click', () => {
if (socket && socket.readyState === WebSocket.OPEN) {
socket.close();
} else {
connectWebSocket();
}
});
// 页面加载时自动连接
window.addEventListener('load', connectWebSocket);
HTML 结构(需配合上述 JS 使用)
完整的 HTML 页面结构,包含 Tailwind CSS 和 Font Awesome 引入:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>WebSocket JSON 消息接收</title>
<script src="https://cdn.tailwindcss.com"></script>
<link href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css" rel="stylesheet">
<script>
tailwind.config = {
theme: {
extend: {
colors: {
primary: '#165DFF',
secondary: '#36CFC9',
neutral: '#F5F7FA',
dark: '#1D2129',
},
fontFamily: {
inter: ['Inter', 'system-ui', 'sans-serif'],
},
}
}
}
</script>
<style type="text/tailwindcss">
@layer utilities {
.content-auto {
content-visibility: auto;
}
.message-card {
@apply bg-white rounded-lg shadow-md p-4 mb-4 transition-all duration-300 hover:shadow-lg border-l-4 border-primary;
}
.error-card {
@apply bg-red-50 border-l-4 border-red-400 text-red-700 p-4 mb-4 rounded-r shadow-md;
}
.socket-status {
@apply fixed top-4 right-4 px-4 py-2 rounded-full text-sm font-medium transition-all duration-300 z-50;
}
.socket-status.connected {
@apply bg-green-100 text-green-800 border border-green-400;
}
.socket-status.disconnected {
@apply bg-red-100 text-red-800 border border-red-400;
}
}
</style>
</head>
<body class="bg-gray-50 font-inter text-dark min-h-screen">
<div class="container mx-auto px-4 py-8 max-w-5xl">
<header class="mb-8">
<h1 class="text-[clamp(1.5rem,3vw,2.5rem)] font-bold text-primary mb-2">WebSocket JSON 消息接收</h1>
<p class="text-gray-600">实时接收并解析后端发送的 JSON 数据</p>
</header>
<!-- 状态指示器 -->
<div id="socket-status" class="socket-status disconnected">
<i class="fa fa-circle-o mr-2"></i>未连接
</div>
<!-- 消息容器 -->
<div class="bg-white rounded-xl shadow-lg p-6 mb-6">
<h2 class="text-xl font-semibold mb-4 flex items-center">
<i class="fa fa-comments-o text-primary mr-2"></i>消息记录
</h2>
<div id="message-container" class="space-y-4 max-h-[60vh] overflow-y-auto p-2">
<div class="text-center text-gray-500 italic py-8">
等待 WebSocket 连接...
</div>
</div>
</div>
<!-- 控制按钮 -->
<div class="flex justify-between items-center">
<button id="connect-btn" class="px-6 py-3 bg-primary hover:bg-primary/90 text-white rounded-lg shadow transition-all duration-300 flex items-center">
<i class="fa fa-plug mr-2"></i>连接
</button>
<div class="text-sm text-gray-500">
<i class="fa fa-clock-o mr-1"></i>最后更新: <span id="last-update">-</span>
</div>
</div>
</div>
<!-- 这里插入上面的 JavaScript 代码 -->
<script>
// 上面的 JS 代码放在这里
</script>
</body>
</html>
核心功能说明
WebSocket 连接管理:
- 使用
WebSocket
构造函数创建连接 - 监听
onopen
、onmessage
、onclose
和onerror
事件 - 提供连接 / 断开切换按钮
JSON 解析:
- 使用
JSON.parse()
解析后端发送的字符串 - 处理嵌套 JSON 结构
- 类型安全检查和错误处理
UI 渲染:
- 使用 Tailwind CSS 实现现代卡片式布局
- 动态生成 HTML 内容
- 添加消息动画和状态指示器
- 响应式设计适配各种屏幕尺寸
用户体验优化:
- 连接状态实时显示
- 消息接收时间戳
- 错误提示和友好反馈
- 平滑过渡动画
将上述代码保存为 HTML 文件后,在浏览器中打开即可使用。确保后端 WebSocket 服务在 ws://localhost:8080/websocket
可用,或根据实际情况修改连接地址。