一、技术简介
Vue.js
这是一个流行的 JavaScript 框架,用于构建用户界面和单页应用程序。它的响应式数据绑定和组件化特性使得构建前端界面更加便捷。
Express框架
Express 是一个基于 Node.js 的 Web 应用程序框架,用于构建后端服务器。它提供了路由、中间件和简化了 HTTP 请求处理的工具,使得构建服务器端应用更加简单和灵活。
WebSocket
WebSocket 是一种在单个 TCP 连接上提供全双工通信的协议。它允许客户端和服务器之间实现持久的、低延迟的连接,适用于实时性要求高的应用,比如聊天应用、实时协作工具等。
二、代码仓库
链接: 仓库地址
三、前端设计及实现
1、登录界面
使用elmentUI提供的表单组件,这里只需要输入用户名即可。
<template>
<div class="body">
<el-form :rules="rules" ref="loginForm" :model="loginForm" class="loginContainer">
<h3 class="loginTitle">
欢迎登录
</h3>
<el-form-item prop="username">
<el-input type="text" v-model="loginForm.username" placeholder="亲,请输入用户名" >
</el-input>
</el-form-item>
<el-button type="primary" style="width:100%" @click="submitLogin">登录</el-button>
</el-form>
</div>
</template>
添加表单验证的规则,首先不能为空其次长度最多1-10个字符,并将用户名存储在sessionStorage,登录成功会跳转到聊天室主页,登录成功或失败都会有对应的消息提示。
export default {
name: "Login",
data(){
return{
captchaUrl: "",
loginForm:{
username:"",
},
checked: true,
rules:{
username:[{required:true,message:"请输入用户名",trigger:"blur"},{ min: 1, max: 10, message: '长度在 1 到 10 个字符', trigger: 'blur' }
],
}
}
},
methods:{
submitLogin(){
var _this = this;
this.$refs.loginForm.validate((valid) => {
if (valid) {
sessionStorage.setItem('username', this.loginForm.username);
_this.$message({
message: '登录成功',
type: 'success'
})
_this.$router.push('/ChatHome');
}
else {
_this.$message.error('登录出错请重新输入');
return false;
}
});
}
}
};
2、聊天室界面
展示了主要的聊天界面,包括用户昵称、头像、时间戳等,其中包括了一些自定义的组件。
<template>
<div class="chat-window">
<div class="top">
<div class="head-pic">
<HeadPortrait :imgUrl="frinedInfo.headImg"></HeadPortrait>
</div>
<div class="info-detail">
<div class="name">{{ frinedInfo.name }}</div>
<div class="detail">{{ frinedInfo.detail }}</div>
</div>
</div>
<div class="botoom">
<div class="chat-content" ref="chatContent">
<div class="chat-wrapper" v-for="item in chatList" :key="item.id">
<div class="chat-friend" v-if="item.uid !== uid">
<div class="chat-text" >
{{ item.msg }}
</div>
<div class="info-time">
<img :src="item.headImg" alt="" />
<span>{{ item.name }}</span>
<span>{{ item.time }}</span>
</div>
</div>
<div class="chat-me" v-else>
<div class="chat-text" >
{{ item.msg }}
</div>
<div class="info-time">
<span>{{ item.name }}</span>
<span>{{ item.time }}</span>
<img :src="item.headImg" alt="" />
</div>
</div>
</div>
</div>
<div class="chatInputs">
<input class="inputs" v-model="inputMsg" @keyup.enter="sendText" />
<div class="send boxinput" @click="sendText">
<img src="@/assets/img/emoji/rocket.png" alt="" />
</div>
</div>
</div>
</div>
</template>
mounted是vue中的一个钩子函数,在初始化页面完成后对尝试对WebSocket进行连接。在连接成功后存储一下服务器生成的userId以便后续区分不同的用户。
mounted() {
this.init();
},
methods: {
init: function () {
if(typeof(WebSocket) === "undefined"){
alert("您的浏览器不支持socket")
}else{
// 实例化socket
this.socket = new WebSocket(this.path)
// 监听socket连接
this.socket.onopen = this.open
// 监听socket错误信息
this.socket.onerror = this.error
// 监听socket消息
this.socket.onmessage = this.getMessage
}
},
open: function () {
console.log("socket连接成功")
},
error: function () {
console.log("连接错误")
},
getMessage: function (msg) {
console.log(JSON.parse(msg.data));
let params = JSON.parse(msg.data);
//如果为第一次收到消息,存取用户id
if(params.type === 0) {
sessionStorage.setItem('userId', params.userId);
this.uid = params.userId;
}
else {
this.chatList.push(params);
this.scrollBottom();
}
},
// 发送消息给被连接的服务端
send: function (params) {
this.socket.send(params)
},
close: function () {
console.log("socket已经关闭")
},
在路由方面添加一个全局导航守卫,用户需要先登录才能聊天。
// 添加全局导航守卫
router.beforeEach((to, from, next) => {
let username = sessionStorage.getItem('username')
let _this = this;
if(to.path === '/Login') {
next();
}else if(username === null) {
alert('请先登录');
next('/Login');
}
else {
next();
}
});
四、后端设计及实现
后端使用了express-ws处理WebSocket请求,需要注意的是发送JSON数据需要先通过JSON.stringify()方法转为字符串,前端处理时再通过JSON.parse()方法转回JSON。这里还定义了一个函数,用于生成用户的Id,为了方便前端区分用户发送的消息和接受的消息。
const express = require('express');
const expressWs = require('express-ws');
const app = express();
expressWs(app);
// 存储连接的客户端
const clients = new Set();
// 处理 WebSocket 请求
app.ws('/chat', (ws, req) => {
// 将新连接的客户端添加到集合中
clients.add(ws);
const clientId = generateClientId();
// 发送欢迎消息给连接的客户端
const params = {
type: 0,
wel: '欢迎来到聊天室',
userId: clientId,
}
ws.send(JSON.stringify(params));
// console.log(clientId);
// 监听消息事件
ws.on('message', (message) => {
// 广播消息给所有客户端
clients.forEach((client) => {
if (ws !== client && client.readyState === client.OPEN) {
client.send(message);
}
});
});
// 监听连接关闭事件
ws.on('close', () => {
// 从集合中移除断开连接的客户端
clients.delete(ws);
});
});
function generateClientId() {
return Math.random().toString(36).substr(2, 9);
}
// 启动服务器
app.listen(3000, () => {
console.log('Server is running');
});
五、界面展示
登录界面
聊天室界面
六、实验总结
在本次项目的完成中,切实地感受到了前后端分离的开发模式,学到了许多之前没接触过的知识,例如:WebSocket的双向通信和应用、前后端交互时产生的跨域问题和如何配置代理、以及Session的会话控制等。同时也让我对vue、node.js这些技术有了更好的理解。十分感谢孟宁老师的指导,孟宁老师深入浅出的教学也我让对网络程序设计产生了浓厚的兴趣。