vue + socket.io实现一个简易聊天室

     vue + vuex + elementUi + socket.io 实现一个简易的在线聊天室,提高自己在对 vue 系列在项目中应用的深度。因为学会一个库或者框架容易,但要结合项目使用一个库或框架就不是那么容易了。功能虽然不多,但还是有收获。设计和实现思路较为拙劣,恳请各位道友指正

可以达到的需求

  • 能查看在线用户列表
  • 能发送和接受消息

使用到的框架和库

  • socket.io 做为实时通讯基础
  • vuex/vue:客户端 Ui 层使用
  • Element-ui:客户端 Ui 组件

类文件关系图

服务端:

客户端:

服务端实现

    实现聊天服务器的相关功能,包含通讯管道的创建、用户加入、消息的接受与转发等。

一、通讯服务建立

    build/server-config.js:聊天服务器的入口

let socketIo = require('socket.io');
let express = require('express'); 
let cxt = require('../src/services-server');

let httpPort = 9001;
let channelId
= 1
let app
= express();

app.get('/',function(req,res){
res.send(
'启动成功:' + httpPort);
});

let server = require('http').createServer(app);
let io
= socketIo(server);
io.on(
'connection',function(socket){
console.log(
'有客户端连接');
cxt.createChannel(channelId
++,socket)
});
server.listen(httpPort);
//用 server 连接
console.log('io listen success !!' + httpPort);

  • 通过 express 创建一个 server 对象,然后利用 socketIo 创建 io 对象
  • 然后通过 io 的 on 方法监听 connection 事件
  • 当有客户端连接时,触发 connection 事件,县立即调用 "服务端上下文 (后面简称 cxt)" 的 createChannel 方法创建一个管道,此时的管道上是没有用户信息的。

二、创建上下文 (服务端上下文)

实现一个聊天室上下文,包含:用户、房间、消息、管道等数组,所以代码都在 service-server 目录中。

  • index.js:聊天室服务端上下文创建入口,创建 context,并初始化房间到上下文中。
  • context.js:聊天室服务端上下文类,用户、房间、消息、管道等类在此中做集中管理。
  • room 目录:包含房间和房间集合的实现
  • channel:服务端与客户端通讯的管道类
    结合 "通讯服务建立" 中的 connectiong 事件的触,其后转到 cxt.createChannel 方法
createChannel (id, socket) {
    let channel = new Channel(id, socket, this)channel.init()
    channel.index = this.channels.length
    this.channels.push(channel)
}

此时会创建一个管道实例,然后初始化管道实例,并将管道添加到管道数组中。以下是初始化管道实例的代码:

init () {
    let self = this
    let roomInfo = this.cxt.room.collections[0]
    this.roomInfo = roomInfo
    this.socket.join('roomId' + roomInfo.id)
    this.socket.emit(this.cxt.eventKeys.emit.sendRooms, roomInfo) /* send 出去一个默认的房间 */
    this.socket.on(this.cxt.eventKeys.client.registerUser, function (id, name) {
      console.log(id + '-' + name + '--' + self.id)self.cxt.createUserById(id, name, self.id)
    }) /** 新用户注册 */
    this.socket.on(this.cxt.eventKeys.client.newMsg, function (msg) { /** 发送消息 */
      self.notifyMsg(msg)
      console.log(msg)
      self.cxt.addMsg(msg)
    })
    this.socket.on(this.cxt.eventKeys.client.closeConn, function () {
      console.log(self.id + '-- 关闭连接')self.cxt.remove(self)
    })
    this.sendUsers()}

在初始化管道实例时做了如下事件:

  • 将通讯 socket 添加一个到房间中,方便后期好广播消息
  • 向当前连接上来的 socket 发送房间信息,设定为第一个房间
  • 监听三个事件:用户注册、新消息、关闭连接。此处都要逻辑处理,可以参考源码。

客户端实现

    主要实现连接服务、注册用户、发送和接受消息的功能。首先以 main.js 为入口,且需要先装配好 vue 相关配件,如 vuex、ElemUi、客户端通讯管道等,然后创建 vue 实例和连接消息服务器,代码如下:

import '../node_modules/bootstrap/dist/css/bootstrap.css'
import Vue from 'vue'
import ElemUi from 'element-ui'
import 'element-ui/lib/theme-default/index.css'
import App from './App'
import * as stores from './store'
import {Keys} from './uitls'
import {getCxt} from './services-client'

let initRoomInfo = Keys.SETROOMINFO
Vue.use(ElemUi)
/ eslint-disable no-new /
new Vue({
store: stores.
default,
el:
'#app',
template:
'<App/>',
components: {App},
created:
function () {
let self
= this
getCxt().createIo(
this, function (roomInfo) {
stores.busCxt.init()
/ 初始化 view 与 service 层的交互层 (业务层) */
self.$store.dispatch(initRoomInfo, roomInfo)
getCxt().refUsers(
function (users) {
stores.busCxt.userCxt.refUsers(users)
})
})
}
})

一、与服务端的通讯

service-client 目录中实例的与消息服务器通讯,其中包含创建用户、接受和发送消息等。一个客户端只能拥有一个消息管道,以下代码是消息管理的创建:

import * as io from 'socket.io-client'
import Context from './context'

let eventKeys = require('../services-uitls/event.keys')
let url
= 'http://localhost:9001/'
let cxt
= null

export function getCxt () {
if (cxt == null) {
cxt
= new Context(url, eventKeys, io)
}
return cxt
}

在 main.js 中的 vue 实例的 created 勾子中调用了 Context 的 createIo 实例方法,用于创建一个与消息服务器的连接,并接受其中房间发送回来的房间信息。然后就初始化业务层。

二、vuex 的结合

     在 store 目录中实现,包含了 vuex 类相关的实现,还有业务层的实现。其中业务层会引用 "客户端通讯管道",而 vuex 实现类有可能会引用业务层相关实现类,以此实现 ui 到 "消息服务器" 的通讯。 store/index.js 代码如下:

import Vuex from 'vuex'
import Vue from 'vue'

import RoomViewCxt from './room/roomViewCxt'
import UserViexCxt from
'./userViewCxt'
import MsgViewCxt from
'./msg/msgViewCxt'
import BusCxt from
'./indexForBus'

let _busCxt = new BusCxt()

let _rvCxt = new RoomViewCxt()
let _uvCxt
= new UserViexCxt(_busCxt.userCxt)
let _mvCxt
= new MsgViewCxt()

let opt = {
state:
null,
getters:
null,
mutations:
null,
actions:
null
}
_rvCxt.use(opt)
_uvCxt.use(opt)
_mvCxt.use(opt)

Vue.use(Vuex)

let store = new Vuex.Store(opt)
export
default store
export const busCxt
= _busCxt / 业务处理上下文 */
export
function getBusCxt () {
return _busCxt
}

三、组件

组件只实现了 用户注册、主界面容器、消息发送和消息接受等。组件只会引用 store 目录中相关类,不会直接引用管道类。

  • Login.vue:用户注册组件
  • HChat.vue:主界面容器组件
  • Message/MsgWriter.vue:发送消息组件
  • Message/MsgList.vue:接受和显示消息列表组件

如何运行实例

  • 源码地址[2019-09-11 更新,由于原下载地址失效]
  • cnpm run install 安装所有的依赖
  • npm run sokcetIo 启动消息服务器
  • npm run dev 启动客户端
  • 示例截图