
前端安装:npm install socket.io-client
服务端安装:npm i express socket.io
app.vue
vue
<template>
<div class="homes">
<div>连接状态:{{ state.connected ? '✅ 已连接' : '❌ 未连接' }}</div>
<button @click="connect">开启连接</button>
<button @click="disconnect">关闭连接</button>
<br><br>
<button @click="generateRandomMessage" :disabled="!state.connected">
生成随机消息
</button>
<div class="message-list">
<div v-for="(msg, i) in state.messages" :key="i" class="message">
<span>[{{ msg.socketID }}] </span>
<span>[{{ msg.time }}] </span>
<span>{{ msg.content }}</span>
</div>
</div>
</div>
</template>
<script setup>
import { onMounted, onUnmounted } from "vue";
import { state, socket } from "../hooks/socket.js";
// 存储事件监听的标识(用于移除)
let messageListener = null;
let historyListener = null;
// 初始化监听(确保只绑定一次)
const initListeners = () => {
// 先移除可能存在的旧监听(关键:防止重复绑定)
if (messageListener) {
socket.off("broadcast_message", messageListener);
}
if (historyListener) {
socket.off("history_messages", historyListener);
}
// 绑定新监听,并保存引用
messageListener = (msg) => {
state.messages.push(msg);
};
socket.on("broadcast_message", messageListener);
historyListener = (history) => {
state.messages = history;
};
socket.on("history_messages", historyListener);
};
// 移除监听(组件卸载时)
const removeListeners = () => {
socket.off("broadcast_message", messageListener);
socket.off("history_messages", historyListener);
messageListener = null;
historyListener = null;
};
// 连接函数
const connect = () => {
if (!state.connected) {
initListeners(); // 连接前初始化监听
socket.connect();
}
};
// 断开函数
const disconnect = () => {
if (state.connected) {
removeListeners(); // 断开时移除监听
socket.disconnect();
}
};
// 生成消息(不变)
const generateRandomMessage = () => {
if (!state.connected) return;
const msg = Fn_text();
socket.emit("new_message", msg);
};
// 随机消息生成函数(不变)
const Fn_text = (min = 5, max = 30) => {
const shortWords = ["风", "云", "花", "草", "人", "心", "走", "看"];
const midWords = ["春天", "夏天", "阳光", "月亮", "星星", "河流"];
const connectWords = ["和", "与", "但", "而", "在", "的"];
const punctuation = ["。", "?", "!"];
let sentence = [];
let currentLen = 0;
const targetLen = Math.floor(Math.random() * (max - min + 1)) + min;
while (currentLen < targetLen) {
const pools = [
{ pool: shortWords, weight: 4 },
{ pool: midWords, weight: 3 },
{ pool: connectWords, weight: 2 }
];
let totalWeight = pools.reduce((sum, p) => sum + p.weight, 0);
let random = Math.random() * totalWeight;
let chosenPool = shortWords;
for (const p of pools) {
random -= p.weight;
if (random <= 0) {
chosenPool = p.pool;
break;
}
}
const word = chosenPool[Math.floor(Math.random() * chosenPool.length)];
if (currentLen + word.length > max) continue;
sentence.push(word);
currentLen += word.length;
}
return sentence.join("") + punctuation[Math.floor(Math.random() * punctuation.length)];
};
// 组件卸载时清理
onUnmounted(() => {
removeListeners();
});
</script>
<style scoped>
/* 样式不变 */
.homes {
padding: 20px;
}
button {
margin: 5px;
padding: 8px 16px;
cursor: pointer;
}
.message-list {
margin-top: 20px;
border: 1px solid #ccc;
padding: 10px;
height: 400px;
overflow-y: auto;
}
.message {
margin: 5px 0;
padding: 5px;
background: #f0f0f0;
}
</style>
socket.js
js
// hooks/socket.js
import { io } from "socket.io-client";
import { reactive } from "vue";
// 用reactive管理状态(确保响应式)
export const state = reactive({
connected: false,
messages: []
});
// 创建Socket实例(自动连接设为false,手动控制)
export const socket = io("http://localhost:3000", {
autoConnect: false, // 关键:禁用自动连接,由按钮控制
cors: {
origin: "http://localhost:5179" // 必须和前端实际端口一致
}
});
// 监听连接状态变化(自动更新state)
socket.on("connect", () => {
state.connected = true;
console.log("✅ 已连接到服务器");
});
socket.on("disconnect", () => {
state.connected = false;
console.log("❌ 已断开连接");
});
服务端的index.js
js
import express from 'express';
import { createServer } from 'node:http';
import { Server } from 'socket.io';
const app = express();
const server = createServer(app);
const io = new Server(server, {
cors: {
origin: "http://localhost:5179", // 必须和前端端口一致
methods: ["GET", "POST"]
}
});
let messageHistory = [];
io.on("connection", (socket) => {
console.log("新用户连接:", socket.id);
// 发送历史消息
socket.emit("history_messages", messageHistory);
// 接收消息并广播
socket.on("new_message", (content) => {
const msg = {
content,
socketID:socket.id,
time: new Date().toLocaleTimeString()
};
messageHistory.push(msg);
io.emit("broadcast_message", msg); // 广播给所有用户
});
socket.on("disconnect", () => {
console.log("用户断开:", socket.id);
});
});
server.listen(3000, () => {
console.log("服务端运行在 http://localhost:3000");
});