当前位置: 首页 > 后端技术 > Node.js

---Vchat—从头到脚,一个社交聊天系统(vue+node+mongodb)

时间:2023-04-04 01:33:00 Node.js

前言项目开始是因为工作需要聊天室功能,但是因为一些原因最终选择了基于xmpp的Strophe编写的协议.js。于是就想用node自己写一套。本来只是想简单的写个聊天页面,写完了还不满意,所以一直在重构(好像可以理解为什么产品经理老是改需求?乛?乛?)。很多东西,比如mongodb,我也是第一次用,之前也只是接触过mysql。于是边学边写,利用业余时间断断续续写了几个月(这次说的是V0.9.0版本,项目还在更新中。。。),包括一整套前后端交互。UI全靠自己的感觉,没有设计天赋(据说主题切换后只有一套主题,设计起来真的很难~),轻喷---。项目中还有很多需要优化和完善的地方。欢迎大家提问题(文末有q群,欢迎一起学习交流)。废话不多说,本文主要讲一下项目的设计过程和部分功能的实现。对项目感兴趣的同学,请移步源码Vchat——从头到脚,一个在线聊天网络应用(vue+node+mongodb)。*这是分界线----------------------------------------深夜码字,最近真冷相关地址在线预览github码云简书知乎前端项目架构技术栈主要用vue全家桶,没什么好说的,脚手架搭建项目,vuex状态管理,vue-router控制路由,axios用于前端和后端交互。后端是基于node的服务,使用express。为什么我不用koa?纯粹是为了方便,因为我对koa不熟悉(捂脸)。聊天最重要的当然是交流。该项目使用socket.io进行前端和后端通信。数据库为mongoDB,主要包括用户、好友、群聊、消息、表情、号码池等。取自预先生成的号码池(号码池存在于mongodb中)。初始指定号码段10000001-10001999为用户码,号码段100001-100999为群聊码。用户可以使用代码或帐号登录。//号池设计*码数*状态1使用过0未使用*类型1用户2群聊*random随机数索引,用于随机搜索某项//用户表主字段*姓名账号*密码*头像头像*签名个性签名*昵称昵称*邮箱邮箱*电话手机*性别性别*泡泡泡*projectTheme项目主题*壁纸聊天壁纸*signUpTime注册时间*lastLoginTime上次登录时间*chatColor聊天文字颜色*省*市*镇县*conversationsList对话列表*cover封面列表注册时需要判断账号是否已经存在,随机获取的code需要在号码池中标记为已使用,用户密码用md5等加密//md5密码加密constmd5=pass=>{//避免多次调用MD5报错letmd5=crypto.createHash('md5');返回md5.update(pass).digest("hex");};login还需要判断用户是否已经注册,支持账号和code两种登录方式。constlogin=(params,callback)=>{//LoginbaseList.users.find({//在mongodb中可以直接使用$or表示or关系$or:[{"name":params.name},{"code":params.name}]}).then(r=>{if(r.length){letpass=md5(params.pass);if(r[0]['pass']===pass){//更新上次登录时间这里直接写Date.now会报错需要Date.now()!!!;baseList.users.update({name:params.name},{lastLoginTime:Date.now()}).then(raw=>{console.log(raw);});callback({code:0,data:{name:r[0].name,photo:r[0].photo}});}else{callback({code:-1});}}else{callback({code:-1});}})};登录权限管理后台设置全局中间件,统一返回无登录状态的api请求:0app.use('/v*'??,(req,res,next)=>{如果(req.session.login){next();}else{if(req.originalUrl==='/v/user/login'||req.originalUrl==='/v/user/signUp'){next();}else{res.json({status:0});}}});前端使用axios统一设置拦截器//httpresponse服务端响应拦截器,拦截未登录和401错误,跳回登录页面获取tokeninstance.interceptors.response.use(response=>{//拦截未记录if(response.data.status===0){router.replace('/');}returnresponse;},error=>{if(error.response){switch(error.response.status){case401://在此处编写清除令牌的代码router.replace('/');}}returnPromise.reject(error.response.data)});在消息vchat中,消息类型包括好友或群申请、回复申请(同意或拒绝)、群通知、聊天消息(文字、图片、表情、文件),实现消息发送之前,需要对一些内容有一个大概的了解socket.io接口详细的api文档可以查看socket.io//所有的消息请求都是基于连接的io.on('connect',onConnect);//发送给当前客户端socket.emit('hello','canyouhearme?',1,2,'abc');//发送给除发送者之外的所有客户端socket.broadcast.emit('broadcast','hellofriends!');//发送给同一个房间'game'中的所有客户端,除了发送者socket.to('game').emit('nicegame',"let'splayagame");//发送给房间'game'end中的所有客户端,包括发送者io.in('game').emit('big-announcement','游戏即将开始');jointheroom加入会话列表中的房间,好友申请成功或加群成功的会话列表会自动添加。但您也可以手动删除或添加,删除后,您将不再收到有关已删除会话的消息(类似于阻止)。//前端发起加入房间的请求this.conversationsList.forEach(v=>{letval={name:this.user.name,time:utils.formatTime(newDate()),avatar:this.user.photo,roomid:v.id};this.$socket.emit('join',val);});//后端收到请求后执行加入操作,记录加入每个房间的成员,并发送回复通知指定房间在线成员socket.on('join',(val)=>{socket.join(val.roomid,()=>{if(OnlineUser[val.name]){return;}OnlineUser[val.name]=socket.id;io.in(val.roomid).emit('joined',OnlineUser);//包括发件人});});多个房间同时加入多个聊天室时会出现问题,socket可以加入多个房间并向指定房间发送消息,但收到消息时不会区分房间。也就是说,所有的房间消息都会一起发送给客户端。所以我们需要区分哪个消息属于哪个房间,并进行分发。这个需要房间ID来过滤,Vchat使用房间ID。mes(r){//只显示来自这个房间的消息if(r.roomid===this.currSation.id){this.chatList.push(Object.assign({},r,{type:'other'}));}}发送消息//前端send(params,type='mess'){//发送消息if(!this.message&&!params){return;}让val={名称:this.user。name,mes:this.message,time:utils.formatTime(newDate()),avatar:this.user.photo,nickname:this.user.nickname,read:[this.user.name],roomid:this.currSation.id,style:'mess',userM:this.user.id};this.chatList.push(Object.assign({},val,{type:'mine'}));//更新视图。$socket.emit('mes',val);this.message='';}//后台收到消息后存入数据库,转发给房间内除发送者以外的其他成员。socket.on('mes',(val)=>{//聊天消息apiList.saveMessage(val);socket.to(val.roomid).emit('mes',val);});messagerecordall所有消息都会存入mongodb,切换房间时会获取历史消息。在当前房间时,只有最新的新闻会附加到dom中,不会从数据库中获取。默认情况下,聊天窗口只显示最近的100条消息,更多消息可以在聊天记录中查看。//前端获取指定房间的历史消息this.$socket.emit('getHistoryMessages',{roomid:v.id,offset:1,limit:100});//后台关联表,分页,排序messages.find({roomid:params.roomid}).populate({path:'userM',select:'签名照片昵称'})//关联用户基本信息。sort({'time':-1}).skip((params.offset-1)*params.limit).limit(params.limit).then(r=>{r.forEach(v=>{//防止用户修改信息,信息不更新}});r.reverse();callback({code:0,data:r,count:count});}).catch(err=>{console.log(err);callback({code:-1});});项目展示首页聊天窗口,拖拽缩放,聊天壁纸和文字颜色设置。个人设置应用空间相关阅读Mongoose基础介绍socket.io文档Vchat主题切换实现方案来自d2-admin交流群,里面有丰富的学习资料^_^写在后面这篇文章主要讲Vchat的整体设计和一些主要功能的实现,其实在写项目的过程中遇到的坑还是蛮多的,比如mongoose联表查询,文件上传等,这里就不细说了,后面会更新当我有时间的时候。如果Vchat对你有帮助,记得star哦^_^。