使用websocket 来实现聊天功能,主要是通过以下几点来实现的
一、使用nodejs来实现后端接口
1、首先进行初始化
const { WebSocketServer } = require("ws"); // 引入WebSocketServer模块 const Websocket = require("ws"); const WebSocket = require("ws"); // 引入WebSocket模块 const onLineList = []; // 定义一个在线用户列表 // 我们的port是8090 const wss = new WebSocket.Server({ port: 8080 }); // 如果有ws就代表初始化成功 if (wss) { console.log("ws初始化成功");
2、进行连接 --- 设置进入的欢迎语
const user = new URL(request.url, `http://${request.headers.host}`).searchParams.get('user'); console.log(`${user} 已连接`); let welCome = JSON.stringify({ type: "tips", content: "欢迎加入聊天室,现在我们开始畅聊吧" }); ws.send(welCome, { binary: false });
2)进行注册消息的事件
ws.on("message", async function message(data) { const message = JSON.parse(data); // 将收到的数据解析为JSON格式 switch ( message.type // 根据消息类型进行不同的处理 ) { case "init": if (message.userId) { // 如果消息中包含用户id // 为当前的用户 ws链接绑定 用户id,用于用户断开连接时,改变用户状态 ws.userId = message.userId; // 上线 keepLatestOnlineList("onLine", message); } break; case "message": wss.clients.forEach(client => { // 遍历所有客户端 if (!message.timestamp) { message.timestamp = new Date().toISOString(); } if (client.readyState === Websocket.OPEN) { // 如果客户端处于打开状态 client.send(JSON.stringify(message), { binary: false }); // 发送消息 } }); break; default: break; } });
3)被动断开说明用户已经离开网站了,维护在线的列表
ws.on("close", function() { keepLatestOnlineList("close", { userId: ws.userId }); }); function keepLatestOnlineList(type, message) { let index = onLineList.findIndex(item => item.userId === message.userId); // 在在线列表中查找用户 switch (type) { case "onLine": if (index === -1) { // 如果用户不在在线列表中,则添加用户 onLineList.push({ userId: message.userId }); } break; case "close": if (index !== -1) { // 如果用户在在线列表中,则删除用户 onLineList.splice(index, 1); console.log("有客户被断开"); } break; default: break; } } }
二、 设置前端页面
1)模板部分
<template> <div class="app"> <div class="chat_container"> <div class="main_content_header">实时聊天</div> <div class="chat-room"> <div class="message-item" v-for="(item, index) in messageList" :key="index"> <div v-if="item.isSystem" class="system-message"> {{ formatSendTime(item.timestamp) }} </div> <!-- 左边通新内容 --> <div class="flex-left" v-if="item.userId !== userInfo.userId"> <!--其他人的 头像 --> <div class="avater"> <el-avatar src="https://picsum.photos/200/300"></el-avatar> </div> <div class="message-content">{{ item.content }}</div> </div> <div class="flex-right" v-else> <!--自己的 头像 --> <div class="message-content">{{ item.content }}</div> <div class="avater"> <el-avatar> <img src="../../../assets/images/avater.jpg" alt=""> </el-avatar> </div> </div> </div> </div> <div class="send-box"> <el-input type="text" v-model="yourMessage" /> <el-button type="primary" @click="sendMesage">发送</el-button> </div> </div> </div> </template>
2)逻辑部分
<script setup > import { onMounted, ref } from 'vue' const yourMessage = ref('') const messageList = ref([]) let ws = null const userInfo = ref({ userId: "" }) const user = ref(sessionStorage.getItem('user')) // 发送消息 function sendMesage() { const timestamp = new Date().toISOString() if (yourMessage.value) { ws.send(JSON.stringify({ type: "message", isSystem: true, userId: userInfo.value.userId, content: yourMessage.value, timestamp: timestamp, // 添加时间戳 }) ) yourMessage.value = '' } } const initWebSocket = () => { ws = new WebSocket(`ws://localhost:4090?user=${String(user.value)}`) ws.onopen = () => { console.log('链接成功') ws.send(JSON.stringify({ type: "init", isSystem: true, userId: userInfo.value.userId, content: '欢迎来到聊天室', }) ) } ws.onmessage = (e) => { const message = JSON.parse(e.data) switch (message.type) { // 根据消息类型进行不同的操作 case "tips": console.log(message.content) // 如果消息类型为tips,则打印消息内容 case 'message': messageList.value.push(message) // 如果消息类型为message,则将消息添加到消息列表中 console.log(messageList.value) // 打印消息列表 break; case 'onlineList': onlineList.value = message.onlineList // 如果消息类型为onlineList,则将在线用户列表赋值给onlineList break; } } ws.onclose = () => { console.log('连接关闭') } ws.onerror = () => { } } onMounted(() => { userInfo.value.userId = new Date().getTime().toString().slice(8) initWebSocket() }) // 时间格式化 const formatSendTime = (sendTime) => { const now = new Date(); const sendDate = new Date(sendTime); const timeDiff = now - sendDate; const oneDay = 24 * 60 * 60 * 1000; if (timeDiff < 0) { return "Invalid time"; // 或者其他错误处理 } if (timeDiff < oneDay) { return "今天" + formatTime(sendDate); } return sendDate.toLocaleDateString("zh-CN") + " " + formatTime(sendDate); }; const formatTime = (date) => { const hours = date.getHours().toString().padStart(2, "0"); const minutes = date.getMinutes().toString().padStart(2, "0"); return hours + ":" + minutes; }; </script>
3)样式部分
<style lang='less'> .app { // width: 100vw; // height: 100vh; overflow: hidden; // background-color: #fff; display: grid; place-items: center; .chat_container { width: 650px; height: 650px; overflow: hidden; background-color: #fff; border-radius: 8px; } } .chat-room { height: calc(100% - 110px); padding: 10px; overflow: auto; background: #000; } .send-box { border-top: 1px solid #eee; display: flex; align-items: center; height: 60px; } .message-item { width: 100%; // margin: 0 auto; margin-bottom: 10px; .flex-left { display: flex; justify-content: flex-start; .avater { width: 40px; height: 40px; border-radius: 50%; overflow: hidden; display: flex; justify-content: center; align-items: center; background-color: #eee; margin-right: 10px; } .message-content { min-height: 30px; height: 40px; line-height: 40px; background: #fff; border-radius: 8px; padding: 0 10px; } } .flex-right { display: flex; justify-content: flex-end; .avater { width: 40px; height: 40px; border-radius: 50%; overflow: hidden; display: flex; justify-content: center; align-items: center; background-color: #eee; margin-left: 10px; } .message-content { min-height: 30px; height: 40px; line-height: 40px; background: #fff; border-radius: 8px; padding: 0 10px; } } } .main_content_header { width: 100%; height: 50px; border-radius: 5px; background-color: #7de0bd; display: flex; align-items: center; justify-content: center; font-size: 16px; } .system-message { font-size: 12px; color: #fff; display: flex; justify-content: center; align-items: center; } </style>
完整代码
nodejs 部分
const { WebSocketServer } = require("ws"); // 引入WebSocketServer模块 const Websocket = require("ws"); const WebSocket = require("ws"); // 引入WebSocket模块 //定义一个在线列表 const onLineList = [] //设置端口号 const wss = new WebSocket.Server({ port: 4090 }); // 如果ws就代表初始化成功 if(wss){ console.log('WebSocket初始化成功') } // 进行连接 wss.on("connection", function connection(ws, request) { // 获取对应的用户名,来进行展示连接 const user = new URL(request.url, `http://${request.headers.host}`).searchParams.get('user'); console.log(`${user} 已连接`) // 设置欢迎语 let welCome = JSON.stringify({ type: "tips", content:"欢迎回来~" }) ws.send(welCome, { binary: false }); ws.on("error", (error) => { console.log("WebSocket error:", error); }) //注册收到消息的事件 ws.on("message", async function message(data) { const message = JSON.parse(data); switch (message.type) { case "init": if (message.userId) { ws.userId = message.userId; keepLatestOnlineList('online',message) } break; case "message": wss.clients.forEach(client => { // 遍历所有的客户端 if(!message.timestamp) { message.timestamp = new Date().toISOString(); } if (client.readyState === Websocket.OPEN) { // 如果客户端处于打开状态 client.send(JSON.stringify(message), { binary: false }); // 发送消息 } }) break; default: break; } }) ws.on("close", function() { keepLatestOnlineList("close", { userId: ws.userId }); }); }) function keepLatestOnlineList(type, message) { let index = onLineList.findIndex(item => item.userId === message.userId); // 在在线列表中查找用户 switch (type) { case "onLine": if (index === -1) { // 如果用户不在在线列表中,则添加用户 onLineList.push({ userId: message.userId }); } break; case "close": if (index !== -1) { // 如果用户在在线列表中,则删除用户 onLineList.splice(index, 1); console.log("有客户被断开"); } break; default: break; } }
完成之后的样式如下
喜欢点个关注吧~