当前位置: 首页 > news >正文

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

完成之后的样式如下

 喜欢点个关注吧~

相关文章:

  • stack overflow国内无法访问原因
  • 中文编码,GB系列,UTF
  • 正则表达式使用知识(日常翻阅)
  • 基于频率约束条件的最小惯量需求评估,包括频率变化率ROCOF约束和频率最低点约束matlab/simulink
  • 探索 Rust 语言:高效、安全与并发的完美融合
  • hashcode() equals()
  • 如何使用阿里云邮件推送免费群发邮件
  • 基本命令题与答案
  • 3 VS Code 配置优化与实用插件推荐:settings.json 详解、CodeGeeX 智能编程助手及插件离线安装方法
  • 企业经营决策风险
  • 在人工智能与计算机技术融合的框架下探索高中教育数字化教学模式的创新路径
  • 系统的安全及应用
  • 【数字电路】第四章 组合逻辑电路
  • 门极驱动器DRV8353M设计(二)
  • 问题 | 针对SSM(Spring + Spring MVC + MyBatis)框架的去Spring MVC强化版学习路线
  • 本地缓存方案Guava Cache
  • 《MySQL是怎样运行的》总结笔记
  • vue入门:template 和 JSX
  • jupyter4.4安装使用
  • 器件封装-2025.4.13
  • 冯德莱恩:欧美贸易谈判前不会前往美国会见特朗普
  • 韩德洙成为韩国执政党总统大选候选人
  • 比特币价格重返10万美元,哪些因素使然?
  • 数理+AI+工程,上海交大将开首届“笛卡尔班”招生约20名
  • 上海发布大风黄警:预计未来24小时内将出现8-10级大风
  • 上海:5月8日起5年以上首套个人住房公积金贷款利率下调至2.6%