使用websocket 来实现聊天功能,主要是通过以下几点来实现的
一、使用nodejs来实现后端接口
1、首先进行初始化
const { WebSocketServer } = require("ws"); // 引入WebSocketServer模块const Websocket = require("ws");const WebSocket = require("ws"); // 引入WebSocket模块const onLineList = []; // 定义一个在线用户列表// 我们的port是8090const 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,则将在线用户列表赋值给onlineListbreak;}}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;} }
完成之后的样式如下
喜欢点个关注吧~