vue3+nodeJs+webSocket实现聊天功能
利用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;
}
}
完成之后的样式如下
https://i-blog.csdnimg.cn/direct/0542c356a8a84a5b92a3f6e1c9acb8aa.png 喜欢点个关注吧~
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页:
[1]