node+vue3+websocket实现简易的聊天功能

后台接口代码

1、首先我们可以通过Express 应用程序生成器快速搭建一个后台框架。(这快可以参考官网)

2、安装ws

npm install  express-ws

3、编写后台代码接口

简单逻辑说明:
1.创建连接
2.每个用户都有一个conn 那把conn存入到数组里方便以后广播所接受到的消息,去传达到其他用户
3.对用户数量做一个统计
4.接收到前端传来设置的用户名

代码如下:
在routes文件下新建一个ws.js文件
node+vue3+websocket实现简易的聊天功能_第1张图片

let express = require("express")
const expressWs = require("express-ws")
let router = express.Router()
expressWs(router)
let count = 0
//存放所有连接的用户
let connections = []
router.ws("/", (conn) => {
  //每个用户都有一个conn
  count++
  console.log(`游客进入聊天室,当前在线人数${count}`)
  sendMsg(conn, "count", count)
  //接收用户发来的数据
  conn.on("message", function (msg) {
    let obj = JSON.parse(msg)
    console.log(obj)
    if (obj.type === "name") {
      conn.userName = obj.author
      connections.push(conn)
      console.log(`用户的名字为${conn.userName},当前在线人数${count}`)
    } else if (obj.type === "submit") {
      broadcast("message", obj)
    }
  })
  //监听关闭状态
  conn.on("close", function () {
    count--
    // broadcast(JSON.stringify({ type: "count", data: count }))
    broadcast(
      JSON.stringify({
        type: "toast",
        data: `${conn.userName}离开了聊天室,当前在线人数${count}`
      })
    )
    console.log(
      `用户:${
        conn.userName ? conn.userName : "游客"
      }离开了聊天室,当前在线人数${count}`
    )
  })
})

function broadcast(type, data) {
  // 群发消息给所有用户
  connections.forEach((item) => {
    item.send(JSON.stringify({ type: type, data: data }))
  })
}
//单独发送给一个用户
function sendMsg(conn, type, data) {
  conn.send(JSON.stringify({ type: type, data: data }))
}
module.exports = router

4、注册路由

在app.js文件注册路由

var wsRouter = require('./routes/ws');
app.use('/wss', wsRouter);

5、bin/www 配置

由于 express-ws 在默认不添加server 参数情况下,使用的是app.listen 创建的httpserver,而express 脚手架将 app和server初始化分离了,所以需要再次配置express-ws

var expressWs = require('express-ws')(app, server);

前端代码

效果展示
node+vue3+websocket实现简易的聊天功能_第2张图片
UI用的Ant Design of Vue Comment组件
类似一个实时评论回复的效果
代码展示:

<template>
    <a-list class="show-box" v-if="comments.length" :data-source="comments" item-layout="horizontal">
        <template #renderItem="{ item }">
            <a-list-item>
                <a-comment :author="item.author" :avatar="item.avatar" :content="item.content" :datetime="item.datetime" />
            </a-list-item>
        </template>
    </a-list>
    <a-comment>
        <template #avatar>
            <a-avatar :src="userInfo?.avatar" />
        </template>
        <template #content>
            <a-form-item>
                <a-textarea v-model:value="value" :rows="4" />
            </a-form-item>
            <a-form-item>
                <a-button html-type="submit" :loading="submitting" type="primary" @click="handleSubmit">
                    Add Comment
                </a-button>
            </a-form-item>
        </template>
    </a-comment>
</template>
<script lang="ts" setup>
import { ref, onUnmounted, onMounted } from 'vue';
import dayjs from 'dayjs';
import relativeTime from 'dayjs/plugin/relativeTime';
dayjs.extend(relativeTime);
type Comment = Record<string, string>;
const userInfo = JSON.parse(localStorage.getItem('userInfo') as string)
// 建立连接
const wsUrl = `ws://${location.host}/socket/wss`
const websock = new WebSocket(wsUrl);
// 建立连接后发送给后台存用户名
websock.onopen = (evt) => {
    console.log("Connection open ...");
    websock.send(JSON.stringify({
        type: 'name',
        author: userInfo?.nickName,
    }));
};
// 监听后台推送的信息(渲染评论)
websock.onmessage = (e) => {
    const res = JSON.parse(e?.data)
    console.log(res);
    if (res.type === 'message') {
        setTimeout(() => {
            submitting.value = false;
            comments.value = [
                ...comments.value,
                res.data,
            ];
            value.value = '';
        }, 1000);
    }
}
const comments = ref<Comment[]>([]);
const submitting = ref<boolean>(false);
const value = ref<string>('');
// 提交评论发送后台
const handleSubmit = () => {
    if (!value.value) {
        return;
    }
    submitting.value = true;
    const obj = {
        type: 'submit',
        author: userInfo?.nickName,
        avatar: userInfo?.avatar,
        content: value.value,
        datetime: dayjs().format('YYYY/MM/DD HH:mm:ss'),
    }
    websock.send(JSON.stringify(obj));
};
// 断开连接
onUnmounted(() => {
    websock.close();
});
</script>
<style lang="less" scoped>
.show-box {
    max-height: 520px;
    overflow: auto;
}
</style>

代理转发
vite.config.js

server:{
    proxy:{
       '/api':{
           target: "http://localhost:3000", //跨域地址
           changeOrigin: true, //支持跨域
           rewrite: (path) => path.replace(/^\/api/, "")//重写路径,替换/api
       },
       '/socket': {
        target: 'ws://localhost:3000',
        ws: true,
        rewrite: (path) => path.replace(/^\/socket/, "")
      },
    }
 }

源码地址

你可能感兴趣的:(websocket,vue,node.js,typescript,html5,anti-design-vue,express)