Vue realizes group chat by configuring websocket

Time:2020-12-23

When writing a jQuery project, using websocket is very simple, without considering modularity and access between components. Document oriented programming can be used. When using in Vue project, it is far less simple than expected, and many scenarios need to be considered. This article will share the use and configuration of Vue native websocket library with developers to realize the group chat function. Let’s take a look at the final results

 

Installation dependency

For the explanation of Vue native websocket Library in this paper, vuex is configured in the project. Developers who do not know about vuex should move to the official documents. If you choose to continue reading this article, it will be difficult.

Vue native websocket installation
#Yarn | NPM installation
yarn add vue-native-websocket | npm install vue-native-websocket --save

Installation successful

Configure plug-ins

In main.js Import from

import VueNativeSock from 'vue-native-websocket'

Use the vuenativesock plug-in and configure it

// main.js
// base.lkWebSocket For your server websocket address
Vue.use(VueNativeSock,base.lkWebSocket,{
 //Enable vuex integration, store value is your vuex
 store: store,
 //The data is sent / received in JSON format
 format: "json",
 //Turn on automatic reconnection
 reconnection: true,
 //Number of attempts to reconnect
 reconnectionAttempts: 5,
 //Reconnection interval
 reconnectionDelay: 3000,
 //The data will be serialized. Since the data transmission in JSON format is enabled, it needs to be rewritten here
 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;
 }
});

Related configuration of vuex: mutation and actions add related functions

//Vuex configuration file
import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex);

export default new Vuex.Store({
 state: {
 token:"",
 userID:"",
 //User Avatar
 profilePicture: "",
 socket: {
 //Connection status
 isConnected: false,
 //Message content
 message: '',
 //Reconnection error
 reconnectError: false
 }
 },
 mutations: {
 SOCKET_ONOPEN (state, event) {
 //Function triggered by connection opening
 Vue.prototype.$socket = event.currentTarget;
 state.socket.isConnected = true
 },
 SOCKET_ONCLOSE (state, event) {
 //Function triggered by connection closure
 state.socket.isConnected = false;
 console.log(event);
 },
 SOCKET_ONERROR (state, event) {
 //Connect function triggered by error
 console.error(state, event)
 },
 SOCKET_ONMESSAGE (state, message) {
 //Function triggered when a message is received
 state.socket.message = message
 },
 SOCKET_RECONNECT(state, count) {
 //Reconnecting the triggered function
 console.info(state, count)
 },
 SOCKET_RECONNECT_ERROR(state) {
 //Function triggered by reconnection failure
 state.socket.reconnectError = true;
 },
 },
 actions: {
 customerAdded (context) {
 //Add new join function
 console.log('action received: customerAdded');
 console.log(context)
 }
 },
 modules: {
 }
})

This is the end of Vue native websocket configuration. For more configuration methods, please move to NPM warehouse

Using plug-ins and realizing group chat

Adding onmessage listening to message sending and receiving components (in the mounted life cycle)

//Listening for message reception
this.$options.sockets.onmessage = (res)=>{
 // res.data Data returned for the server
 const data = JSON.parse(res.data);
 //200 is the status code returned when the connection between the server and the server is successfully established (this is modified according to the real back-end return value)
 if(data.code===200){
 //Connection established successfully
 console.log(data.msg);
 }else{
 //Get the message pushed by the server
 const msgObj = {
 msg: data.msg,
 avatarSrc: data.avatarSrc,
 userID: data.userID
 };
 //Render page: if msgarray exists, switch to JSON
 if(lodash.isEmpty(localStorage.getItem("msgArray"))){
 this.renderPage([],msgObj,0);
 }else{
 this.renderPage(JSON.parse(localStorage.getItem("msgArray")),msgObj,0);
 }
 }
};

Realize message sending

//Message sending function
sendMessage: function (event) {
 if (event.keyCode === 13) {
 //Prevent edit box from generating div events by default
 event.preventDefault();
 let msgText = "";
 //Gets all the child elements under the input box
 let allNodes = event.target.childNodes;
 for(let item of allNodes){
 //Determine whether the current element is an img element
 if(item.nodeName==="IMG"){
 msgText += `/${item.alt}/`;
 }
 else{
 //Gets the value of the text node
 if(item.nodeValue!==null){
 msgText += item.nodeValue;
 }
 }
 }
 //Message sending: message content, status code, avatar address of current login user, user ID
 this.$socket.sendObj({msg: msgText,code: 0,avatarSrc: this.$store.state.profilePicture,userID: this.$store.state.userID});
 //Clear the contents of the input box
 event.target.innerHTML = "";
 }
}

Realize page rendering

//Render page functions
renderPage: function(msgArray,msgObj,status){
 if(status===1){
 //The page is loaded for the first time and rendered to the page if there is data in the local storage
 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
 };
 //Parse and render
 this.messageParsing(thisSenderMessageObj);
 }
 }
 }else{
 //Determine whether there is data in local storage
 if(localStorage.getItem("msgArray")===null){
 //New records
 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,
 };
 //Parse and render
 this.messageParsing(thisSenderMessageObj);
 }
 }else{
 //Update records
 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
 };
 //Parse and render
 this.messageParsing(thisSenderMessageObj);
 }
 }
}

Implement message parsing

//Message parsing
messageParsing: function(msgObj){
 //Parse the data returned by the interface for rendering
 let separateReg = /(\/[^/]+\/)/g;
 let msgText = msgObj.msgText;
 let finalMsgText = "";
 //Put the qualified string into the array
 const resultArray = msgText.match(separateReg);
 if(resultArray!==null){
 for (let item of resultArray){
 //Delete / symbol in string
 item = item.replace(/\//g,"");
 for (let emojiItem of this.emojiList){
 //Determine whether the captured string is the same as the string in the configuration file
 if(emojiItem.info === item){
 const imgSrc = require(`../assets/img/emoji/${emojiItem.hover}`);
 const imgTag = `<img src="${imgSrc}" width="28" height="28" alt="${item}">`;
 //Replace matching string to img label: Global substitution
 msgText = msgText.replace(new RegExp(`/${item}/`,'g'),imgTag);
 }
 }
 }
 finalMsgText = msgText;
 }else{
 finalMsgText = msgText;
 }
 msgObj.msgText = finalMsgText;
 //Render the page
 this.senderMessageList.push(msgObj);
 //Modify scroll bar position
 this.$nextTick(function () {
 this.$refs.messagesContainer.scrollTop = this.$refs.messagesContainer.scrollHeight;
 });
}

DOM structure

The user ID of each message and the current user’s userid stored in vuex are used to determine whether the current message is sent by the other party

<! -- message display -- >
<div ref="messagesContainer">
 <div v-for="item in senderMessageList" :key="item.msgId">
 <! -- sender message style -- >
 <div v-if="item.userID===userID">
 <! -- message -- >
 <div>
 <! -- message tail -- >
 <div>
 <svg aria-hidden="true">
 <use xlink:href="#icon-zbds30duihuakuangyou" rel="external nofollow" ></use>
 </svg>
 </div>
 <! -- message content -- >
 <p v-html="item.msgText"/>
 </div>
 <! -- head portrait -- >
 <div>
 <img :src="item.avatarSrc" alt="">
 </div>
 </div>
 <! -- message style of the other party -- >
 <div v-else>
 <! -- head portrait -- >
 <div>
 <img :src="item.avatarSrc" alt="">
 </div>
 <! -- message -- >
 <div>
 <! -- message tail -- >
 <div>
 <svg aria-hidden="true">
 <use xlink:href="#icon-zbds30duihuakuangzuo" rel="external nofollow" ></use>
 </svg>
 </div>
 <! -- message content -- >
 <p v-html="item.msgText"/>
 </div>
 </div>
 </div>
</div>

summary

The above is a brief introduction to Vue, through the configuration of websocket and the realization of group chat function, I hope to help you!