npm install express-ws
简单逻辑说明:
1.创建连接
2.每个用户都有一个conn 那把conn存入到数组里方便以后广播所接受到的消息,去传达到其他用户
3.对用户数量做一个统计
4.接收到前端传来设置的用户名
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
在app.js文件注册路由
var wsRouter = require('./routes/ws');
app.use('/wss', wsRouter);
由于 express-ws 在默认不添加server 参数情况下,使用的是app.listen 创建的httpserver,而express 脚手架将 app和server初始化分离了,所以需要再次配置express-ws
var expressWs = require('express-ws')(app, server);
效果展示
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/, "")
},
}
}
源码地址