原文链接:
写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" >
|