前言


项目开始是因为工作需要一个聊天室功能,但是因为某些原因最终选用的是基于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});}})};

登录权限管理

  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});}}});
  1. 前端用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');
  1. 加入房间

加入会话列表中的房间,会话列表在好友申请成功或者加群成功时会自动添加。但是你也可以手动移除或添加,移除后将不会再收到被移除会话的消息(类似于屏蔽)。

    // 前端 发起加入房间的请求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); // 包括发送者});});
  1. 多房间

同时加入多个聊天房间会出现一个问题,socket可以加入多个房间并给指定房间发送消息,但是接受消息的时候并不会区分房间。换句话说,所有房间的消息,会一起发送给客户端。所以我们需要自己区分哪条消息是哪个房间的并进行分发。这样就需要一个房间标识来过滤,Vchat用的是房间id。

    mes(r) { // 只有本房间的消息才展示if (r.roomid === this.currSation.id) {this.chatList.push(Object.assign({}, r, {type: 'other'}));}}
  1. 发消息
    // 前端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);});
  1. 消息记录

所有的消息都会存到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)相关推荐

  1. 实践:《从头到脚撸一个多人视频聊天 — 前端 WebRTC 实战(一)》

    2019独角兽企业重金招聘Python工程师标准>>> 请先阅读原文,链接:从头到脚撸一个多人视频聊天 - 前端 WebRTC 实战(一),本文只涉及实践过程中的问题 1.video ...

  2. 从头到脚撸一个多人视频聊天 — WebRTC 实战(一)

    作者:江三疯,知乎.掘金账号同名,点击阅读原文查看作者 github. 前言 [ 从头到脚 ]会作为一个系列文章来发布,它包括但不限于 WebRTC 多人视频,预计会有: WebRTC 实战(一):也 ...

  3. 撸一个聊天室(vue+koa2+websokect+mongodb)

    撸一个聊天室(vue+koa2+websokect+mongodb) 本篇博客主要介绍聊天室项目,作者学习vue和node时间较短,若有什么错误或建议,欢迎指出,谢谢~ 贴上源码链接 -> 源码 ...

  4. vue+node+mongodb 搭建一个完整博客

    Vue + Node + Mongodb 开发一个完整博客流程 前言 前段时间刚把自己的个人网站写完, 于是这段时间因为事情不是太多,便整理了一下,写了个简易版的博客系统 服务端用的是 koa2框架 ...

  5. 一个 Vue + Node + MongoDB 博客系统

    源码 耗时半载(半个月)的大项目终于完成了.这是一个博客系统,使用 Vue 做前端框架,Node + express 做后端,数据库使用的是 MongoDB.实现了用户注册.用户登录.博客管理(文章的 ...

  6. 【从头到脚】撸一个多人视频聊天 — 前端 WebRTC 实战(一)

    前言 [ 从头到脚 ]会作为一个系列文章来发布,它包括但不限于 WebRTC 多人视频,预计会有: WebRTC 实战(一):也就是本期,主要是基础讲解以及一对一的本地对等连接,网络对等连接. Web ...

  7. 桑叶黑芝麻糊,从头到脚通补

    人体通补手册:丹道医学中的养命之术/武国忠著. -南京:江苏人民出版社,2009.8(国医健康绝学系列)ISBN 978-7-214-05938-3 桑叶黑芝麻糊,从头到脚通补 桑叶味甘.苦,性寒,可 ...

  8. c++ byte*长度_9.19秋冬原C日标95白鹅绒超保暖柔软大被子羽绒服 从头到脚的温暖...

    ................................................................................ 9.19日15:00 开拍预定定金 定 ...

  9. 用Kotlin撸一个图片压缩插件ImageSlimming-导学篇(一)

    简述: 很久没有发布Kotlin的实战相关的内容,这段时间在折腾Intellij IDEA的插件开发,折腾出了几个小插件,因为最近公司业务分离,原来堆在基础业务那边模块,都以模块的形式抽离出来,独立仓 ...

最新文章

  1. numpy 笔记: random模块
  2. opensuse 禁用触摸板
  3. 不改变原数组的一些方法
  4. IP、ARP、RARP、ICMP、IGMP(网络协议:网络层协议)
  5. hive整合ldap权限管理
  6. 中国电子科技集团公司第三十八研究所(合肥9月29日)
  7. 安卓手机与电脑无线传输文件(利用ftp服务)
  8. Laravel 代码开发最佳实践
  9. 纯HTML+CSS静态百度登录界面制作
  10. 英雄联盟登陆服务器显示外文,英雄联盟手游进不去出现英文怎么办 进不去出现英文解决方法[多图]...
  11. 10019---访问远程Redis服务。Connect to Remote Redis Server
  12. 侏罗纪怪兽世界怎么登陆服务器未响应,全金属怪物一直登入不进去怎么办
  13. 10 大话设计模式C++实现之模板方法模式
  14. pytorch断点续传
  15. 学习笔记(07):JavaWeb基础核心技术-11. 佟刚_JavaWEB_GenericServlet
  16. 用DIV+CSS技术设计的凤阳旅游网站(web前端网页制作课作业)HTML+CSS+JavaScript
  17. 近期edge、谷歌浏览器崩溃的问题解决方案
  18. 荣获2021 CSDN年度社区之星,PPDE郑博培的成长之旅
  19. 技术视角看行业SaaS
  20. 编译android sdk方法

热门文章

  1. 【随心所记】矩阵A的行列式不等于0,是A可逆的充要条件吗?答:是这样的
  2. 2019最新《传智教育黑马java项目实战》
  3. 0.96寸OLED(二)SPI+DMA刷新OLED
  4. myeclipse部署时An internal error occurred 错误的几种情况
  5. 【论文翻译】HCGN:面向集体分类的异构图卷积网络深度学习模型
  6. 离散数学复习笔记——图的着色
  7. java-php-python-springboot线上教学平台计算机毕业设计
  8. 笑出腹肌,程序员从不撒谎,但注释却会
  9. 硬盘分区修复和碎片整理命令
  10. 计算机科学引论2021英文,计算机科学引论= Computer Science Illuminated : 第3版 : 英文...