码库记事本

码库记事本

Vue通过配置WebSocket并实现群聊功能

小诸哥 0

原文链接:

写jquery项目时,使用Websocket很简单,不用去考虑模块化,组件之间的访问问题,面向文档编程即可,在vue项目中使用时,远远没有想象中的那么简单,需要考虑很多场景,本篇文章将与各位开发者分享下 vue-native-websocket 库的使用以及配置,用其实现群聊功能。先看下最终实现的效果

 

安装依赖

本文中对于 vue-native-websocket 库的讲解,项目中配置了vuex,对其不了解的开发者请移步官方文档,如果选择继续阅读本篇文章会比较吃力。

1
2
3
vue-native-websocket安装
# yarn | npm 安装
yarn add vue-native-websocket | npm install vue-native-websocket --save

安装成功

配置插件

在 main.js 中进行导入

import vuenativesock from 'vue-native-websocket'

使用 vuenativesock 插件,并进行相关配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// main.js
// base.lkwebsocket为你服务端websocket地址
vue.use(vuenativesock,base.lkwebsocket,{
 // 启用vuex集成,store的值为你的vuex
 store: store,
 // 数据发送/接收使用使用json格式
 format: "json",
 // 开启自动重连
 reconnection: true,
 // 尝试重连的次数
 reconnectionattempts: 5,
 // 重连间隔时间
 reconnectiondelay: 3000,
 // 将数据进行序列化,由于启用了json格式的数据传输这里需要进行重写
 passtostorehandler: function (eventname, event) {
 if (!eventname.startswith('socket_')) { return }
 let method = 'commit';
 let target = eventname.touppercase();
 let msg = event;
 if (this.format === 'json' && event.data) {
 msg = json.parse(event.data);
 if (msg.mutation) {
 target = [msg.namespace || '', msg.mutation].filter((e) => !!e).join('/');
 } else if (msg.action) {
 method = 'dispatch';
 target = [msg.namespace || '', msg.action].filter((e) => !!e).join('/');
 }
 }
 this.store[method](target, msg);
 this.store.state.socket.message = msg;
 }
});

vuex的相关配置:mutations和actions添加相关函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
// vuex配置文件
import vue from 'vue'
import vuex from 'vuex'
 
vue.use(vuex);
 
export default new vuex.store({
 state: {
 token:"",
 userid:"",
 // 用户头像
 profilepicture: "",
 socket: {
 // 连接状态
 isconnected: false,
 // 消息内容
 message: '',
 // 重新连接错误
 reconnecterror: false
 }
 },
 mutations: {
 socket_onopen (state, event) {
 // 连接打开触发的函数
 vue.prototype.$socket = event.currenttarget;
 state.socket.isconnected = true
 },
 socket_onclose (state, event) {
 // 连接关闭触发的函数
 state.socket.isconnected = false;
 console.log(event);
 },
 socket_onerror (state, event) {
 // 连接发生错误触发的函数
 console.error(state, event)
 },
 socket_onmessage (state, message) {
 // 收到消息时触发的函数
 state.socket.message = message
 },
 socket_reconnect(state, count) {
 // 重新连接触发的函数
 console.info(state, count)
 },
 socket_reconnect_error(state) {
 // 重新连接失败触发的函数
 state.socket.reconnecterror = true;
 },
 },
 actions: {
 customeradded (context) {
 // 新连接添加函数
 console.log('action received: customeradded');
 console.log(context)
 }
 },
 modules: {
 }
})

至此 vue-native-websocket 配置结束,如需了解更多配置方法,请移步 npm仓库

使用插件并实现群聊

在消息发送接收组件中添加 onmessage 监听(mounted生命周期中)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 监听消息接收
this.$options.sockets.onmessage = (res)=>{
 // res.data为服务端返回的数据
 const data = json.parse(res.data);
 // 200为服务端连接建立成功时返回的状态码(此处根据真实后端返回值进行相应的修改)
 if(data.code===200){
 // 连接建立成功
 console.log(data.msg);
 }else{
 // 获取服务端推送的消息
 const msgobj = {
 msg: data.msg,
 avatarsrc: data.avatarsrc,
 userid: data.userid
 };
 // 渲染页面:如果msgarray存在则转json
 if(lodash.isempty(localstorage.getitem("msgarray"))){
 this.renderpage([],msgobj,0);
 }else{
 this.renderpage(json.parse(localstorage.getitem("msgarray")),msgobj,0);
 }
 }
};

实现消息发送

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// 消息发送函数
sendmessage: function (event) {
 if (event.keycode === 13) {
 // 阻止编辑框默认生成div事件
 event.preventdefault();
 let msgtext = "";
 // 获取输入框下的所有子元素
 let allnodes = event.target.childnodes;
 for(let item of allnodes){
 // 判断当前元素是否为img元素
 if(item.nodename==="img"){
 msgtext += `/${item.alt}/`;
 }
 else{
 // 获取text节点的值
 if(item.nodevalue!==null){
 msgtext += item.nodevalue;
 }
 }
 }
 // 消息发送: 消息内容、状态码、当前登录用户的头像地址、用户id
 this.$socket.sendobj({msg: msgtext,code: 0,avatarsrc: this.$store.state.profilepicture,userid: this.$store.state.userid});
 // 清空输入框中的内容
 event.target.innerhtml = "";
 }
}

实现页面渲染

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
// 渲染页面函数
renderpage: function(msgarray,msgobj,status){
 if(status===1){
 // 页面第一次加载,如果本地存储中有数据则渲染至页面
 let msgarray = [];
 if(localstorage.getitem("msgarray")!==null){
 msgarray = json.parse(localstorage.getitem("msgarray"));
 for (let i = 0; i<msgarray.length;i++){
 const thissendermessageobj = {
 "msgtext": msgarray[i].msg,
 "msgid": i,
 "avatarsrc": msgarray[i].avatarsrc,
 "userid": msgarray[i].userid
 };
 // 解析并渲染
 this.messageparsing(thissendermessageobj);
 }
 }
 }else{
 // 判断本地存储中是否有数据
 if(localstorage.getitem("msgarray")===null){
 // 新增记录
 msgarray.push(msgobj);
 localstorage.setitem("msgarray",json.stringify(msgarray));
 for (let i = 0; i <msgarray.length; i++){
 const thissendermessageobj = {
 "msgtext": msgarray[i].msg,
 "msgid": i,
 "avatarsrc": msgarray[i].avatarsrc,
 "userid": msgarray[i].userid,
 };
 // 解析并渲染
 this.messageparsing(thissendermessageobj);
 }
 }else{
 // 更新记录
 msgarray = json.parse(localstorage.getitem("msgarray"));
 msgarray.push(msgobj);
 localstorage.setitem("msgarray",json.stringify(msgarray));
 const thissendermessageobj = {
 "msgtext": msgobj.msg,
 "msgid": date.now(),
 "avatarsrc": msgobj.avatarsrc,
 "userid": msgobj.userid
 };
 // 解析并渲染
 this.messageparsing(thissendermessageobj);
 }
 }
}

实现消息解析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// 消息解析
messageparsing: function(msgobj){
 // 解析接口返回的数据进行渲染
 let separatereg = /(\/[^/]+\/)/g;
 let msgtext = msgobj.msgtext;
 let finalmsgtext = "";
 // 将符合条件的字符串放到数组里
 const resultarray = msgtext.match(separatereg);
 if(resultarray!==null){
 for (let item of resultarray){
 // 删除字符串中的/符号
 item = item.replace(/\//g,"");
 for (let emojiitem of this.emojilist){
 // 判断捕获到的字符串与配置文件中的字符串是否相同
 if(emojiitem.info === item){
 const imgsrc = require(`../assets/img/emoji/${emojiitem.hover}`);
 const imgtag = `<img src="${imgsrc}" width="28" height="28" alt="${item}">`;
 // 替换匹配的字符串为img标签:全局替换
 msgtext = msgtext.replace(new regexp(`/${item}/`,'g'),imgtag);
 }
 }
 }
 finalmsgtext = msgtext;
 }else{
 finalmsgtext = msgtext;
 }
 msgobj.msgtext = finalmsgtext;
 // 渲染页面
 this.sendermessagelist.push(msgobj);
 // 修改滚动条位置
 this.$nexttick(function () {
 this.$refs.messagescontainer.scrolltop = this.$refs.messagescontainer.scrollheight;
 });
}

dom结构

通过每条消息的userid和vuex中的存储的当前用户的userid来判断当前消息是否为对方发送

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
<!--消息显示-->
<div class="messages-panel" ref="messagescontainer">
 <div class="row-panel" v-for="item in sendermessagelist" :key="item.msgid">