基于 Vue3 + WebSocket 实现的平板控制端与大屏展示端联动方案

包含断线重连逻辑,采用「控制端 - 服务器 - 展示端」架构,通过 Vue 的响应式特性管理连接状态和指令交互。

技术栈

  • 服务器:Node.js + ws 库(WebSocket 服务)
  • 控制端 / 展示端:Vue3(Composition API) + 原生 WebSocket API
  • 构建工具:Vite(快速开发体验)

实现步骤

1. 服务器端(转发与连接管理)

与前方案核心逻辑一致,负责维护连接、转发指令和心跳检测(复用 server.js):

javascript 复制代码
// server.js
const WebSocket = require('ws');
const wss = new WebSocket.Server({ port: 8080 });

const clients = { controller: null, display: null }; // 存储两端连接
const HEARTBEAT_INTERVAL = 30000; // 30秒心跳检测

wss.on('connection', (ws) => {
  let clientType = null;

  ws.on('message', (data) => {
    const msg = JSON.parse(data.toString());
    if (msg.type === 'register') {
      clientType = msg.role;
      clients[clientType] = ws;
      ws.send(JSON.stringify({ type: 'connected', msg: `已作为${clientType}连接` }));
      startHeartbeat(ws);
    } else if (clientType === 'controller' && msg.type === 'control') {
      // 转发控制指令到展示端
      if (clients.display?.readyState === WebSocket.OPEN) {
        clients.display.send(JSON.stringify({ type: 'control', data: msg.data }));
      } else {
        ws.send(JSON.stringify({ type: 'error', msg: '展示端未连接' }));
      }
    }
  });

  ws.on('close', () => {
    if (clientType) clients[clientType] = null;
  });

  function startHeartbeat(ws) {
    const timer = setInterval(() => {
      if (ws.readyState === WebSocket.OPEN) {
        ws.send(JSON.stringify({ type: 'heartbeat' }));
      } else {
        clearInterval(timer);
      }
    }, HEARTBEAT_INTERVAL);
  }
});

console.log('WebSocket服务器运行于 ws://localhost:8080');
2. 控制端(平板 Vue 应用)

使用 Vue3 的 refonMounted 管理连接状态,通过按钮发送控制指令,包含断线自动重连逻辑。

(1)创建控制端项目
bash 复制代码
npm create vite@latest controller -- --template vue
cd controller
npm install
(2)核心组件 src/App.vue
javascript 复制代码
<template>
  <div class="controller">
    <h1>平板控制端</h1>
    <p class="status" :class="{ connected: isConnected }">
      状态:{{ statusText }}
    </p>

    <div class="controls">
      <button @click="sendControl('showText', 'Hello 大屏!')">
        显示文本
      </button>
      <button @click="sendControl('changeColor', randomColor)">
        切换背景色
      </button>
      <button @click="sendControl('clear', null)">
        清空内容
      </button>
    </div>
  </div>
</template>

<script setup>
import { ref, onMounted, onUnmounted } from 'vue';

// 状态管理
const ws = ref(null);
const isConnected = ref(false);
const statusText = ref('未连接');
const SERVER_URL = 'ws://localhost:8080';
const RECONNECT_DELAY = 3000; // 重连间隔3秒
let reconnectTimer = null;

// 生成随机颜色
const randomColor = () => {
  return `#${Math.floor(Math.random() * 16777215).toString(16)}`;
};

// 初始化WebSocket连接
const initWebSocket = () => {
  // 关闭现有连接
  if (ws.value) {
    ws.value.close();
  }

  statusText.value = '连接中...';
  ws.value = new WebSocket(SERVER_URL);

  // 连接成功
  ws.value.onopen = () => {
    isConnected.value = true;
    statusText.value = '已连接';
    // 注册为控制端
    ws.value.send(JSON.stringify({ type: 'register', role: 'controller' }));
    // 清除重连定时器
    if (reconnectTimer) clearTimeout(reconnectTimer);
  };

  // 接收服务器消息
  ws.value.onmessage = (event) => {
    const msg = JSON.parse(event.data);
    if (msg.type === 'error') {
      statusText.value = `错误:${msg.msg}`;
    }
  };

  // 连接关闭(触发重连)
  ws.value.onclose = () => {
    isConnected.value = false;
    statusText.value = '已断开,重连中...';
    // 延迟重连,避免频繁尝试
    reconnectTimer = setTimeout(initWebSocket, RECONNECT_DELAY);
  };

  // 连接错误
  ws.value.onerror = (err) => {
    console.error('WebSocket错误:', err);
    statusText.value = '连接错误';
  };
};

// 发送控制指令
const sendControl = (action, data) => {
  if (ws.value?.readyState === WebSocket.OPEN) {
    ws.value.send(JSON.stringify({
      type: 'control',
      data: { action, payload: data }
    }));
  } else {
    statusText.value = '未连接,无法发送指令';
  }
};

// 组件挂载时初始化连接
onMounted(initWebSocket);

// 组件卸载时清理
onUnmounted(() => {
  if (ws.value) ws.value.close();
  if (reconnectTimer) clearTimeout(reconnectTimer);
});
</script>

<style scoped>
.controller {
  padding: 20px;
  text-align: center;
}
.status {
  font-size: 18px;
  margin: 20px 0;
  color: #ff4444;
}
.status.connected {
  color: #00C851;
}
.controls {
  display: flex;
  flex-direction: column;
  gap: 15px;
  margin-top: 30px;
}
button {
  padding: 15px 20px;
  font-size: 16px;
  cursor: pointer;
  background: #2196F3;
  color: white;
  border: none;
  border-radius: 8px;
}
button:hover {
  background: #0b7dda;
}
</style>
3. 展示端(大屏 Vue 应用)

接收控制端指令并执行对应操作(更新文本、切换样式等),同样包含断线重连逻辑。

(1)创建展示端项目
bash 复制代码
npm create vite@latest display -- --template vue
cd display
npm install
(2)核心组件 src/App.vue
javascript 复制代码
<template>
  <div class="display">
    <h1>大屏展示端</h1>
    <p class="status" :class="{ connected: isConnected }">
      状态:{{ statusText }}
    </p>

    <div 
      class="display-area" 
      :style="{ backgroundColor: bgColor }"
    >
      {{ displayText }}
    </div>
  </div>
</template>

<script setup>
import { ref, onMounted, onUnmounted } from 'vue';

// 状态管理
const ws = ref(null);
const isConnected = ref(false);
const statusText = ref('未连接');
const displayText = ref('等待控制指令...');
const bgColor = ref('');
const SERVER_URL = 'ws://localhost:8080';
const RECONNECT_DELAY = 3000;
let reconnectTimer = null;

// 初始化WebSocket连接
const initWebSocket = () => {
  if (ws.value) ws.value.close();

  statusText.value = '连接中...';
  ws.value = new WebSocket(SERVER_URL);

  ws.value.onopen = () => {
    isConnected.value = true;
    statusText.value = '已连接';
    ws.value.send(JSON.stringify({ type: 'register', role: 'display' }));
    if (reconnectTimer) clearTimeout(reconnectTimer);
  };

  // 接收控制指令并处理
  ws.value.onmessage = (event) => {
    const msg = JSON.parse(event.data);
    if (msg.type === 'control') {
      handleControl(msg.data);
    }
  };

  ws.value.onclose = () => {
    isConnected.value = false;
    statusText.value = '已断开,重连中...';
    reconnectTimer = setTimeout(initWebSocket, RECONNECT_DELAY);
  };

  ws.value.onerror = (err) => {
    console.error('WebSocket错误:', err);
    statusText.value = '连接错误';
  };
};

// 处理控制指令
const handleControl = (data) => {
  switch (data.action) {
    case 'showText':
      displayText.value = data.payload;
      break;
    case 'changeColor':
      bgColor.value = data.payload;
      break;
    case 'clear':
      displayText.value = '已清空';
      bgColor.value = '';
      break;
  }
};

onMounted(initWebSocket);
onUnmounted(() => {
  if (ws.value) ws.value.close();
  if (reconnectTimer) clearTimeout(reconnectTimer);
});
</script>

<style scoped>
.display {
  padding: 30px;
  text-align: center;
}
.status {
  font-size: 20px;
  margin: 20px 0;
  color: #ff4444;
}
.status.connected {
  color: #00C851;
}
.display-area {
  margin: 50px auto;
  padding: 60px 30px;
  width: 80%;
  min-height: 300px;
  border: 5px solid #333;
  border-radius: 10px;
  font-size: 36px;
  transition: all 0.3s ease;
}
</style>

核心特性说明

1. 响应式状态管理
  • 控制端 / 展示端均通过 Vue 的 ref 管理连接状态(isConnected)、状态文本(statusText)等,界面实时响应状态变化。
  • 展示端的 displayTextbgColor 与 DOM 绑定,接收指令后自动更新视图,体现 Vue 数据驱动的优势。
2. 断线重连机制
  • 自动重连 :两端在 onclose 事件中触发重连逻辑,通过 setTimeout 实现 3 秒间隔重试,直到连接成功。
  • 连接清理 :组件卸载时(onUnmounted)关闭 WebSocket 连接并清除重连定时器,避免内存泄漏。
  • 状态可视化:通过颜色区分连接状态(绿色 = 已连接,红色 = 未连接 / 错误),用户可直观感知。
3. 指令交互流程
  1. 控制端点击按钮 → 调用 sendControl 发送指令(包含 actionpayload)。
  2. 服务器接收指令 → 转发给展示端。
  3. 展示端 onmessage 接收指令 → 调用 handleControl 执行对应操作(更新文本 / 颜色)。

运行方式

  1. 启动 WebSocket 服务器:

    bash 复制代码
    node server.js
  2. 分别启动控制端和展示端:

    bash 复制代码
    # 控制端
    cd controller
    npm run dev
    # 访问 http://localhost:5173(平板或浏览器模拟)
    
    # 展示端
    cd display
    npm run dev
    # 访问 http://localhost:5174(大屏或浏览器模拟)
  3. 操作控制端按钮,观察大屏是否同步响应;断开服务器再重启,测试重连功能。

扩展性优化

  • 多设备支持 :修改服务器 clients 为数组,存储多个控制端 / 展示端,通过设备 ID 区分指令目标。
  • 指令加密:对传输的指令进行简单加密(如 Base64),防止恶意指令干扰。
  • 心跳响应 :展示端 / 控制端收到心跳包后回复 heartbeat_ack,增强连接稳定性检测。

此方案充分利用 Vue3 的响应式和组件化特性,结合 WebSocket 实现了低延迟的跨设备联动,适合会议、展览等场景的实时控制需求。

相关推荐
u0103055272 天前
AI驱动的安卓UI自动生成功能实现
人工智能·1024程序员节
u0103055273 天前
长株潭智能体赋能腾讯元器链接共享
1024程序员节
云贝贝贝4 天前
OceanBase认证体系介绍与备考指南
运维·服务器·oceanbase·1024程序员节
u0103055275 天前
Java图像处理实战指南
人工智能·1024程序员节
u0103055275 天前
Java AWT鼠标事件全解析
人工智能·1024程序员节
u0103055277 天前
Java实用类应用精讲
人工智能·1024程序员节
u0103055278 天前
Java I/O核心操作实战
人工智能·1024程序员节
u0103055279 天前
ArrayList操作详解与实战应用
人工智能·1024程序员节
u0103055279 天前
Java模块化编程实战指南
人工智能·笔记·1024程序员节
u01030552710 天前
使用BufferedReader读取控制台输入
人工智能·1024程序员节