基于Vue、Nodejs、Socket.io的聊天应用

image.png

写在最前

一直想实现一个聊天应用demo,尝试一下Socket.IO这个框架,同时看到网上的教程很多关于使用node开发聊天应用的demo都是聊天室形式的,也就是群聊,很少有实现私聊的。所以想自己实现一次。另外还尝试了在上传头像的时候接入腾讯云的万象优图API来上传下载注册用户的头像,事实证明确实蛮好用的=。=,只不过那个部署接口的服务器不知道什么时候就会没钱租了,所以可能并不能跑通上传功能T T。github地址

一、技术简介

Vue

Vue.js是一款在2014年2月开源的前端MVVM开发框架,其内容核心为为开发者提供简洁的API,并且通过实现高效的数据绑定来构建一个可复用的组件系统。

Nodejs

Node.js是一个基于Chrome JavaScript运行时建立的平台, 用于方便地搭建响应速度快、易于扩展的网络应用。Node.js 使用事件驱动, 非阻塞I/O 模型而得以轻量和高效,非常适合在分布式设备上运行数据密集型的实时应用。

MongoDB

MongoDB是一个基于分布式文件存储的数据库。由C++语言编写。旨在为WEB应用提供可扩展的高性能数据存储解决方案。

Socket.IO

Socket.IO是一个完全由JavaScript实现、基于Node.js、支持WebSocket的协议用于实时通信、跨平台的开源框架。
Socket.IO除了支持WebSocket通讯协议外,还支持许多种轮询(Polling)机制以及其它实时通信方式,并封装成了通用的接口,并且在服务端实现了这些实时机制的相应代码。
Socket.IO会根据浏览器对通讯机制的支持情况,选择最佳方式实现网络实时通信。

二、功能

  • [x] 登陆
  • [x] 注册
  • [x] 通讯录
  • [x] 聊天列表
  • [x] 私聊
  • [x] 群聊
  • [x] 好友管理

三、重点功能实现

上传图片配合腾讯云

图片预览问题

因浏览器的安全限制,在input中提交的文件是不能获得文件的真实地址的这就导致无法直接通过地址来得到所需要的预览图。故可以使用一个HTML5API,FileReader对象。https://developer.mozilla.org/zh-CN/docs/Web/API/FileReader

image.png

上传到腾讯云


腾讯云接口的部署分为两个部分,一个是自己服务器做鉴权服务,另一个是腾讯服务器来存储图片。由图可以看到第一步需要从自己服务器中获取上传头像的地址,这一步就是鉴权服务,详细代码可以参照腾讯云官方文档。之后的如何上传到腾讯云也是使用官方的接口。文档相对详细,同时如果出现bug还可以发起工单向tx的工程师咨询。那次我的bug那边的工程师晚上11点给我打电话来解决=。=,所以如果你有一个程序员老公/老婆,请好好珍惜

私聊&群聊

本次私聊和群聊的界面由同一个组件复用,在前端页面需要判断渲染私聊还是群聊消息。同时后端也需要判断是群聊还是私聊消息模式。
通讯的前端逻辑如下所示:


image.png

通讯的后端逻辑如下所示:


image.png

开始实现

  1. 前端,后端部署 Socket.IO
// client.js 前端部署socket.io
import io from 'socket.io-client'
const CHAT={
  ...
  init:function(username){
    //连接websocket后端服务器
    this.socket = io.connect('http://127.0.0.1:3000',{'force new connection': true})
    this.socket.on('open', function() {
      console.log('已连接')
    })
    this.socket.emit('addUser', username)
  }
}
export default CHAT
// server/bin/www 后端部署socket.io
var io = require('socket.io')(server)
io.on('connection', function(socket){
  ...
  socket.emit('open')
  ...
})
socket.on("disconnect", function () {
  console.log("客户端断开连接.")
  delete '某一个对应socket对象' 
  //每次都要删除该socket连接 否则断开重连还是这个socket但是client端socket已经改变
})

部署之后前后端均可使用emit和on来发送和监听自定义事件。socket.io文档

  1. 前端区分渲染私聊群聊
// src/component/talk.vue
<div v-for="msgObj in CHAT.msgArr" track-by="$index">
    <div  class="talk-space self-talk" 
        v-if="CHAT.msgArr[$index].fromUser == username && CHAT.msgArr[$index].toUser == $route.query.username" 
        track-by="$index">
    <div class="talk-content">
      <div class="talk-word talk-word-self">{{ msgObj.msg }}</div><i class="swip"></i>
    </div>
    </div>
    <div v-else></div>
    <div  class="talk-space user-talk" 
        v-if="CHAT.msgArr[$index].toUser == username && CHAT.msgArr[$index].fromUser == $route.query.username" 
        track-by="$index">
    <div class="talk-content">
      <div v-if="CHAT.msgArr[$index].fromUser =='群聊'" class="talk-all">{{ msgObj.trueFrom }}</div>
      <div class="talk-word talk-word-user">
        {{ msgObj.msg }}
        <i class="swip-user"></i>
      </div>
    </div>
</div>

可以看到在CHAT.msgArr维护了一个公共消息队列,这个队列里面有群聊也有私聊的消息。同时每一个数组对象里面都含有fromUSer,toUser方法,来作为记录发送消息的人和接受消息的人的区分。再配合路由中携带的用户名来判断该消息应该渲染在界面的左侧还是右侧。同时由于群聊中虽然用户所面对的聊天对象是“群聊”,但是在渲染左侧“群聊”发送的消息时,仍然应该渲染出真实的用户名即msgObj.trueFrom字段。

3.后端通讯逻辑

// server/bin/www
io.on('connection', function(socket){
  var toUser = {}
  var fromUser = {}
  var msg = ''
  socket.emit('open')
  socket.on('addUser', function(username) {
    if(!onlineUsers.hasOwnProperty(username)) {
        onlineUsers[username] = socket
        onlineCount = onlineCount + 1
    }
    user = username
    console.log(onlineUsers[username].id) //建立连接后 用户点击不同通讯录都是建立同样的socket对象
    console.log('在线人数:',onlineCount)
      socket.on('sendMsg', function(obj) {
        toUser = obj.toUser
        fromUser = obj.fromUser
        msg = obj.msg
        time = obj.time
        if (toUser == '群聊') { //判断为群聊模式
            obj.fromUser = '群聊'
            obj.toUser = user
            obj.trueFrom = fromUser //携带真实发送方
          for (user in onlineUsers) { //遍历所有对象,区分自己和其他用户
            if( user != fromUser ) { //接收方
              onlineUsers[user].emit('to' + user, obj)
            } else { //发送方
              obj.toUser = '群聊'
              obj.fromUser = user
              onlineUsers[fromUser].emit('to' + fromUser, obj)
            }
          }
        } 
        else if(toUser in onlineUsers) { //私聊模式
          onlineUsers[toUser].emit('to' + toUser, obj)
          onlineUsers[fromUser].emit('to' + fromUser, obj)
        } else {
          console.log(toUser + '不在线')
          onlineUsers[fromUser].emit('to' + fromUser, obj)
        }
      })
    socket.on("disconnect", function () {
      console.log("客户端断开连接.")
      //遇到的坑 每次都要删除该socket连接 否则断开重连还是这个socket但是client端socket已经改变
      delete onlineUsers[fromUser] 
    })
  })
})

效果如下图:



最后

这是一个最最最基本的聊天demo实现,也是我第一次自己写一个小分享。ui方面借用了网页版wx的部分样式。同时代码中仍然存在一些“不可预知”的bug,比如聊天消息显示有时候会出问题但是并没有好的方法来排查,主要是第一次使用vue来做前端框架,里面的html和js分离我还是不能很习惯,在debug方面做得还不够好,毕竟用了很久的react..嗯这都不是理由,所以欢迎交流心得,bug可提issues,虽然我可能...
最后po一个github地址:https://github.com/Aaaaaaaty/vue-im
博客地址:https://github.com/Aaaaaaaty/Blog

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,284评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,115评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,614评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,671评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,699评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,562评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,309评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,223评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,668评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,859评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,981评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,705评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,310评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,904评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,023评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,146评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,933评论 2 355

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 172,138评论 25 707
  • 九月十九日,长安雨。秋天的列车终于缓缓到站了,阵阵秋雨带来满满的寒意。 社团接手接近一个月,问题种种。 社团和学生...
    柠檬味葡萄阅读 220评论 0 0
  • 因为国庆假期和中秋节放在一起,让这个节日氛围变的异常浓烈。回家的,旅游的.,探亲访友的...... 我也借这个机会...
    妖精红袖阅读 415评论 3 8
  • 2016年,社会进入了全民入网的时代,网络已经几乎将所有人连接起来了,而2016年,又被后世称作:网络元年(我杜撰...
    谭大鹏阅读 697评论 0 0