写在最前
一直想实现一个聊天应用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
上传到腾讯云
腾讯云接口的部署分为两个部分,一个是自己服务器做鉴权服务,另一个是腾讯服务器来存储图片。由图可以看到第一步需要从自己服务器中获取上传头像的地址,这一步就是鉴权服务,详细代码可以参照腾讯云官方文档。之后的如何上传到腾讯云也是使用官方的接口。文档相对详细,同时如果出现bug还可以发起工单向tx的工程师咨询。那次我的bug那边的工程师晚上11点给我打电话来解决=。=,所以如果你有一个程序员老公/老婆,请好好珍惜
私聊&群聊
本次私聊和群聊的界面由同一个组件复用,在前端页面需要判断渲染私聊还是群聊消息。同时后端也需要判断是群聊还是私聊消息模式。
通讯的前端逻辑如下所示:
image.png
通讯的后端逻辑如下所示:
image.png
开始实现
- 前端,后端部署 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文档
- 前端区分渲染私聊群聊
// 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
网友评论