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;
  }
}

完成之后的样式如下

喜欢点个关注吧~

相关推荐
一一Null1 小时前
关于手机取证中逻辑采集与系统备份的差异
服务器·网络·智能手机
码农新猿类1 小时前
服务器本地搭建
linux·网络·c++
张太行_2 小时前
UDP目标IP不存在时的发送行为分析
网络协议·tcp/ip·udp
SlientICE3 小时前
TCP是什么?不需要!使用ESP32网络层直接通信!
网络·单片机·网络协议·tcp/ip
帽儿山的枪手3 小时前
通过网络命名空间实现网络分流的思想及方法
网络协议·docker·dns
sky.fly3 小时前
在思科模拟器show IP route 发现Gateway of last resort is not set没有设置最后的通道
网络协议·tcp/ip·gateway
小李超勇的..4 小时前
SOME/IP
网络·网络协议·tcp/ip
EasyDSS5 小时前
WebRTC实时通话EasyRTC嵌入式音视频通信SDK,构建智慧医疗远程会诊高效方案
大数据·网络·网络协议·音视频
左灯右行的爱情5 小时前
计算机网络-传输层基础概念
网络·计算机网络·php
游戏开发爱好者85 小时前
Flutter 学习之旅 之 flutter 使用 shared_preferences 实现简单的数据本地化保存封装
websocket·网络协议·tcp/ip·http·网络安全·https·udp