Vchat — 从头到脚,撸一个社交聊天系统(vue + node + mongodb)
前言
项目开始是因为工作需要一个聊天室功能,但是因为某些原因最终选用的是基于xmpp协议的Strophe.js写的。于是就想用node自己写一套,本来只是想简单的写个聊天页面,但是写完了又不满意,所以不断的重构(似乎可以理解产品经理为什么老是改需求了๑乛◡乛๑)。
很多东西,比如mongodb,我也是第一次用,以前只接触过mysql。所以都是一边学一边写,利用工作之余的时间,断断续续的写了几个月(这次讲的是V0.9.0版本,项目还在更新中···),包含了一整套的前后端交互。uI是按照自己的感觉来的,没有设计天分(话说主题切换到现在还只有一套主题,实在是不好设计啊~),轻喷—。项目还有很多需要优化完善的地方,欢迎大家提到issues(文末有q群,欢迎一起学习交流)。
闲话少说,本文主要讲项目的设计流程,以及部分功能实现思路。对项目感兴趣的同学请移步源码 Vchat — 从头到脚,撸一个在线聊天的web应用(vue + node + mongodb)。
*这是分隔线---------------------------------------深夜码字,最近真冷
相关地址
- 在线预览
- github
- 码云
- 简书
- 知乎
项目架构
- 技术栈
前端主要采用了vue全家桶,没什么多说的,脚手架构建项目,vuex状态管理,vue-router控制路由,axios进行前后端交互。后端是基于node搭的服务,用的是express。我为什么不用koa呢,纯粹是图方便,因为koa不熟(捂脸)。聊天最重要的当然是通信,项目用socket.io来进行前后端通信。
数据库是mongoDB,主要有用户、好友、群聊、消息、表情、号码池等。
- 功能概览
功能设计
- 登录注册
Vchat中用户注册时,会随机指定一个code号码,而这个code号是从预先生成的一个号码池(号码池存在mongodb)中取的。初始指定10000001-10001999的号码段为用户code, 100001-100999的号码段为群聊code。用户可以凭借code号或者账号登录。
// 号码池设计* code 号码* status 1 已使用 0 未使用* type 1 用户 2 群聊* random 随机数索引,用于随机查找某一条// user表主要字段* name 账号* pass 密码* avatar 头像* signature 个性签名* nickname 昵称* email 邮件* phone 手机* sex 性别* bubble 气泡* projectTheme 项目主题* wallpaper 聊天壁纸* signUpTime 注册时间* lastLoginTime 最后一次登录时间* chatColor 聊天文字颜色* province 省* city 市* town 县* conversationsList 会话列表* cover 封面列表
注册时,需要判断账号是否已存在,以及随机取得的code需要在号码池中标记为已被使用,用户密码用md5加密等。
// md5 密码加密const md5 = pass => { // 避免多次调用MD5报错let md5 = crypto.createHash('md5');return md5.update(pass).digest("hex");};
登录同样需要判断用户是否已注册,以及支持账号和code两种方式登录。
const login = (params, callback) => { // 登录baseList.users.find({ // mongodb中可以直接用$or表示或关系$or: [{"name": params.name}, {"code": params.name}]}).then(r => {if (r.length) {let pass = 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请求统一返回status: 0
app.use('/v*', (req, res, next) => {if (req.session.login) {next();} else {if (req.originalUrl === '/v/user/login' || req.originalUrl === '/v/user/signUp') {next();} else {res.json({status: 0});}}});
- 前端用axios统一设置拦截器
// http response 服务器响应拦截器,这里拦截未登录和401错误,并重新跳入登页重新获取token
instance.interceptors.response.use(response => { // 拦截未登录if (response.data.status === 0) {router.replace('/');}return response;},error => {if (error.response) {switch (error.response.status) {case 401:// 这里写清除token的代码router.replace('/');}}return Promise.reject(error.response.data)});
- 消息
vchat中,消息种类包括好友或者加群申请、回复申请(同意or拒绝)、入群通知、聊天消息(文字、图片、表情、文件)
在实现消息发送之前,需要大体的了解一些
socket.io
的api。详细api文档可以查看socket.io
// 所有的消息请求都是建立在已连接的基础上的io.on('connect', onConnect);// 发送给当前客户端socket.emit('hello', 'can you hear me?', 1, 2, 'abc');// 发送给所有客户端,除了发送者socket.broadcast.emit('broadcast', 'hello friends!');// 发送给同在 'game' 房间的所有客户端,除了发送者socket.to('game').emit('nice game', "let's play a game");// 发送给同在 'game' 房间的所有客户端,包括发送者io.in('game').emit('big-announcement', 'the game will start soon');
- 加入房间
加入会话列表中的房间,会话列表在好友申请成功或者加群成功时会自动添加。但是你也可以手动移除或添加,移除后将不会再收到被移除会话的消息(类似于屏蔽)。
// 前端 发起加入房间的请求this.conversationsList.forEach(v => {let val = {name: this.user.name,time: utils.formatTime(new Date()),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可以加入多个房间并给指定房间发送消息,但是接受消息的时候并不会区分房间。换句话说,所有房间的消息,会一起发送给客户端。所以我们需要自己区分哪条消息是哪个房间的并进行分发。这样就需要一个房间标识来过滤,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;}let val = {name: this.user.name,mes: this.message,time: utils.formatTime(new Date()),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'})); // 更新视图this.$socket.emit('mes', val);this.message = '';}// 后端 接收消息后存储到数据库,并转发给房间内其他成员,不包括发送者。socket.on('mes', (val) => { // 聊天消息apiList.saveMessage(val);socket.to(val.roomid).emit('mes', val);});
- 消息记录
所有的消息都会存到mongodb中,当切换房间的时候,会获取历史消息。而处在当前房间时,只会把最新消息追加到dom中,不会从数据库获取。聊天窗口默认只展示最新100条消息,更多消息可在聊天记录中查看。
// 前端 获取指定房间的历史消息this.$socket.emit('getHistoryMessages', {roomid: v.id, offset: 1, limit: 100});// 后端 关联表、分页、排序messages.find({roomid: params.roomid}).populate({path: 'userM', select: 'signature photo nickname'}) // 关联用户基本信息.sort({'time': -1}).skip((params.offset - 1) * params.limit).limit(params.limit).then(r => {r.forEach(v => { // 防止用户修改资料后,信息未更新if (v.userM) {v.nickname = v.userM.nickname;v.photo = v.userM.photo;v.signature = v.userM.signature;}});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一下哟_。
Vchat — 从头到脚,撸一个社交聊天系统(vue + node + mongodb)相关推荐
- 实践:《从头到脚撸一个多人视频聊天 — 前端 WebRTC 实战(一)》
2019独角兽企业重金招聘Python工程师标准>>> 请先阅读原文,链接:从头到脚撸一个多人视频聊天 - 前端 WebRTC 实战(一),本文只涉及实践过程中的问题 1.video ...
- 从头到脚撸一个多人视频聊天 — WebRTC 实战(一)
作者:江三疯,知乎.掘金账号同名,点击阅读原文查看作者 github. 前言 [ 从头到脚 ]会作为一个系列文章来发布,它包括但不限于 WebRTC 多人视频,预计会有: WebRTC 实战(一):也 ...
- 撸一个聊天室(vue+koa2+websokect+mongodb)
撸一个聊天室(vue+koa2+websokect+mongodb) 本篇博客主要介绍聊天室项目,作者学习vue和node时间较短,若有什么错误或建议,欢迎指出,谢谢~ 贴上源码链接 -> 源码 ...
- vue+node+mongodb 搭建一个完整博客
Vue + Node + Mongodb 开发一个完整博客流程 前言 前段时间刚把自己的个人网站写完, 于是这段时间因为事情不是太多,便整理了一下,写了个简易版的博客系统 服务端用的是 koa2框架 ...
- 一个 Vue + Node + MongoDB 博客系统
源码 耗时半载(半个月)的大项目终于完成了.这是一个博客系统,使用 Vue 做前端框架,Node + express 做后端,数据库使用的是 MongoDB.实现了用户注册.用户登录.博客管理(文章的 ...
- 【从头到脚】撸一个多人视频聊天 — 前端 WebRTC 实战(一)
前言 [ 从头到脚 ]会作为一个系列文章来发布,它包括但不限于 WebRTC 多人视频,预计会有: WebRTC 实战(一):也就是本期,主要是基础讲解以及一对一的本地对等连接,网络对等连接. Web ...
- 桑叶黑芝麻糊,从头到脚通补
人体通补手册:丹道医学中的养命之术/武国忠著. -南京:江苏人民出版社,2009.8(国医健康绝学系列)ISBN 978-7-214-05938-3 桑叶黑芝麻糊,从头到脚通补 桑叶味甘.苦,性寒,可 ...
- c++ byte*长度_9.19秋冬原C日标95白鹅绒超保暖柔软大被子羽绒服 从头到脚的温暖...
................................................................................ 9.19日15:00 开拍预定定金 定 ...
- 用Kotlin撸一个图片压缩插件ImageSlimming-导学篇(一)
简述: 很久没有发布Kotlin的实战相关的内容,这段时间在折腾Intellij IDEA的插件开发,折腾出了几个小插件,因为最近公司业务分离,原来堆在基础业务那边模块,都以模块的形式抽离出来,独立仓 ...
最新文章
- numpy 笔记: random模块
- opensuse 禁用触摸板
- 不改变原数组的一些方法
- IP、ARP、RARP、ICMP、IGMP(网络协议:网络层协议)
- hive整合ldap权限管理
- 中国电子科技集团公司第三十八研究所(合肥9月29日)
- 安卓手机与电脑无线传输文件(利用ftp服务)
- Laravel 代码开发最佳实践
- 纯HTML+CSS静态百度登录界面制作
- 英雄联盟登陆服务器显示外文,英雄联盟手游进不去出现英文怎么办 进不去出现英文解决方法[多图]...
- 10019---访问远程Redis服务。Connect to Remote Redis Server
- 侏罗纪怪兽世界怎么登陆服务器未响应,全金属怪物一直登入不进去怎么办
- 10 大话设计模式C++实现之模板方法模式
- pytorch断点续传
- 学习笔记(07):JavaWeb基础核心技术-11. 佟刚_JavaWEB_GenericServlet
- 用DIV+CSS技术设计的凤阳旅游网站(web前端网页制作课作业)HTML+CSS+JavaScript
- 近期edge、谷歌浏览器崩溃的问题解决方案
- 荣获2021 CSDN年度社区之星,PPDE郑博培的成长之旅
- 技术视角看行业SaaS
- 编译android sdk方法
热门文章
- 【随心所记】矩阵A的行列式不等于0,是A可逆的充要条件吗?答:是这样的
- 2019最新《传智教育黑马java项目实战》
- 0.96寸OLED(二)SPI+DMA刷新OLED
- myeclipse部署时An internal error occurred 错误的几种情况
- 【论文翻译】HCGN:面向集体分类的异构图卷积网络深度学习模型
- 离散数学复习笔记——图的着色
- java-php-python-springboot线上教学平台计算机毕业设计
- 笑出腹肌,程序员从不撒谎,但注释却会
- 硬盘分区修复和碎片整理命令
- 计算机科学引论2021英文,计算机科学引论= Computer Science Illuminated : 第3版 : 英文...