vue项目使用SockJS插件实现webSocket通信

背景:公司业务需求,在官网上面需添加客服答疑页面,一般都是使用websocket通信,主要是它是一个低延迟、全双工、跨域通信通道。

注:页面底部有效果图

一、简单介绍

sockjs-client
sockjs-client是从SockJS中分离出来的用于客户端使用的通信模块,是一个浏览器的JavaScript库。主要是为了浏览器的兼容性,有些浏览器是不支持websocket,Spring框架提供了基于SockJS协议的透明的回退选项,也就是说一旦不支持这种模式,SockJS会自动降为轮询的方式。

stomjs
STOMP(Simple Text-Orientated Messaging Protocol) 面向消息的简单文本协议;
WebSocket是一个消息架构,不强制使用任何特定的消息协议,它依赖于应用层解释消息的含义。
与HTTP不同,WebSocket是处在TCP上非常薄的一层,会将字节流转化为文本/二进制消息,因此,对于实际应用来说,WebSocket的通信形式层级过低,因此,可以在 WebSocket 之上使用STOMP协议,来为浏览器和server间的 通信增加适当的消息语义。

STOMP与WebSocket 的关系:

HTTP协议解决了web浏览器发起请求以及web服务器响应请求的细节,假设HTTP协议不存在,只能使用TCP套接字来编写web应用,你可能认为这是一件疯狂的事情;
直接使用WebSocket(SockJS)就很类似于使用TCP套接字来编写web应用,因为没有高层协议,就需要我们定义应用间发送消息的语义,还需要确保连接的两端都能遵循这些语义;
同HTTP在TCP套接字上添加请求-响应模型层一样,STOMP在WebSocket之上提供了一个基于帧的线路格式层,用来定义消息语义。

二、安装插件

npm install sockjs-client
npm install stompjs

三、页面代码

html代码

      <!-- 聊天弹框 主要是客户聊天的页面代码 -->
      <el-dialog
        ref="chatDialog"
        :visible.sync="chatDialog"
        width="360px"
        class="chatDialog"
        :close-on-click-modal="false"
        :close="chatClose"
        title="国瑞在线"
      >
        <div ref="chatContentRef" :contenteditable="false" class="chatContent clearfix" />
        <div class="chating">
          <a href="javascript:void(0)" class="emtionImg" :contenteditable="false" @click="emtionShow()"><img src="@/assets/images/index/icon_index_service_emtion.png" alt=""></a>
          <emotion v-if="emtionIsShow" class="emotion" :height="138" @emotion="handleEmotion" />
          <el-upload
            class="upload"
            action="https://jsonplaceholder.typicode.com/posts/"
            auto-upload
            multiple
            :before-upload="beforeUpload"
          >
            <img src="@/assets/images/index/icon_index_service_img.png" alt="">
          </el-upload>
          <div
            ref="textarea"
            class="textarea"
            :contenteditable="contenteditable"
            @keyup.enter="sendMessage()"
            @keydown.enter.prevent
            @blur="focusContent"
            v-html="content"
          />
          <div class="chatBtn">
            <el-button class="chatBtn_1" @click="closeChatDialog()">结束会话</el-button>
            <el-button class="chatBtn_2" @click="sendMessage()">发送</el-button>
          </div>
        </div>
        <img src="@/assets/images/index/icon_index_service_logo.png" alt="">
      </el-dialog>
      <!-- 图片弹框 -->
      <el-dialog
        ref="previewDialog"
        :visible.sync="previewDialog"
        :width="imgWidth"
      >
        <img :src="imgSrc" alt="">
      </el-dialog>
      <!-- 结束会话弹框 -->
      <el-dialog
        :visible.sync="closeChatCheckDialog"
        width="240px"
        class="closeChatCheck"
      >
        <span>是否结束当前会话</span>
        <span slot="footer" class="dialog-footer">
          <el-button type="primary" @click="closeChatCheck()"></el-button>
          <el-button @click="closeChatCheckDialog = false"></el-button>
        </span>
      </el-dialog>

JS代码

import Emotion from '../../../components/Emotion/index'; //表情包的使用
import SockJS from 'sockjs-client';
import Stomp from 'stompjs';
export default {
    data(){
        return {
            stompClient:'',
            timer:'',
        }
    },
    methods:{
        init() {
            if (typeof (WebSocket) === 'undefined') {
		        alert('您的浏览器不支持socket');
		      } else {
		        this.connection();
      		}
        },  
        connection() {
            // 建立连接对象
            let socket = new SockJS('http://192.168.1.102:9632/web-websocket/webSocketServer');
            // 获取STOMP子协议的客户端对象
            this.stompClient = Stomp.over(socket);
            // 向服务器发起websocket连接
            const userId = Number(Math.random().toString().substr(3, 8) + Number(Date.now().toString().substr(3, 8))).toString(36);
            this.user = {   //随机生成用户ID
		        username: userId
		      };
            this.stompClient.connect(this.user,() => {
                this.stompClient.subscribe('/user/' + userId + '/queue/getResponse', (response) => { // 订阅服务端提供的某个topic
                if (JSON.parse(response.body) && JSON.parse(response.body).type === 'NOTICE' && JSON.parse(response.body).status === 2) {
	              this.users = JSON.parse(response.body).userName;
	              this.contenteditable = true;
	              const noticeMsg = `<div style="margin-top: 20px;text-align:center;margin-bottom: 10px;">您已成功连接</div>`;
	              this.$refs.chatContentRef.innerHTML += noticeMsg;
	            } else if (JSON.parse(response.body) && JSON.parse(response.body).type === 'NOTICE' && JSON.parse(response.body).status === 1) {
	              const noticeMsg = `<div style="margin-top: 20px;text-align:center;margin-bottom: 10px;">当前连接人数较多,请耐心稍等</div>`;
	              this.$refs.chatContentRef.innerHTML += noticeMsg;
	              this.contenteditable = false;
	            }
	            if (JSON.parse(response.body).text) {
	              this.$refs.chatContentRef.innerHTML +=
	            `<div class="clearfix"><div style="float:left"> <span style="margin-right:8px;">卑微客服在线答疑 ${this.$verify.NowDate3()}</span></div> </div>
	              <div class="clearfix">
	                <div style="background-color:#FFFFFF;color:black;padding:8px 8px;text-align:rigth;margin-top:8px;border-radius:6px;
	                float:left"> ${JSON.parse(response.body).text}</div>
	              </div>
	              <br />`;
	              this.$refs.chatContentRef.scrollTop = this.$refs.chatContentRef.scrollHeight;
	            }
	            console.log(response, '订阅成功'); // msg.body存放的是服务端发送给我们的信息
	         });
            }, (err) => {
                // 连接发生错误时的处理函数
                console.log('失败',err);
            });
        },    
        disconnect() {// 断开连接
            if (this.stompClient) {
                this.stompClient.disconnect();
            }
        },
      	sendMessage() {  //发送消息
	      const text = this.$refs.textarea.innerHTML;
	      var msg = {
	        'text': this.$refs.textarea.innerHTML,
	        'userName': this.users
	      };
	      this.stompClient.send('/sendUser', {}, JSON.stringify(msg)); //发送消息的主要代码
	      if (text === '') {
	        return;
	      }
	      const chatContentRef = this.$refs.chatContentRef;
	      const time = this.$verify.NowDate3();
	      chatContentRef.innerHTML +=
	      `<div class="clearfix"><div style="float:right"> <span style="margin-right:8px;">我</span>${time}</div> </div>
	      <div class="clearfix">
	        <div style="background-color:#5595FF;color:#fff;padding:8px 8px;text-align:rigth;margin-top:8px;margin-left:52px;border-radius:6px;
	        float:right">${text} </div>
	      </div>
	      <br />`;
	      chatContentRef.scrollTop = chatContentRef.scrollHeight;
	      this.$refs.textarea.innerHTML = '';
	      this.content = '';
	    },
	    closeChatDialog() {
	      this.closeChatCheckDialog = true;
	    },
	    chatClose() {
	      this.$refs.chatContentRef.innerHTML = '';
	    },
	    emtionShow() {
	      const that = this;
	      that.emtionIsShow = !that.emtionIsShow;
	      this.$refs.textarea.focus();
	    },
	    handleEmotion(i) {
	      this.lastEditRange.deleteContents(); // 删除文档的区域
	      var el = document.createElement('div');
	      el.innerHTML = this.emotion(i);
	      var frag = document.createDocumentFragment(); // 创建一个虚拟的dom
	      var node;
	      var lastNode;
	      while ((node = el.firstChild)) {
	        lastNode = frag.appendChild(node);
	      }
	      this.lastEditRange.insertNode(frag);
	      // Preserve the selection
	      if (lastNode) {
	        this.lastEditRange = this.lastEditRange.cloneRange();
	        this.lastEditRange.setStartAfter(lastNode);
	        this.lastEditRange.collapse(true);
	        this.selection.removeAllRanges();
	        this.selection.addRange(this.lastEditRange);
	      }
	    },
	    // 将匹配结果替换表情图片
	    emotion(res) {
	      const word = res.replace(/\#|\;/gi, '');
	      return `<img style="width:20px;" src="https://twemoji.maxcdn.com/v/12.1.4/72x72/${word}.png" align="middle">`;
	    },
	    focusContent() {
	      this.selection = window.getSelection();
	      this.lastEditRange = this.selection.getRangeAt(0);
	    },
	    beforeUpload(val) {
	      const file = val;
	      const reader = new FileReader();
	      reader.readAsDataURL(file);
	      const chatContentRef = this.$refs.chatContentRef;
	      const time = this.$verify.NowDate3();
	      const stompClient = this.stompClient;
	      const users = this.users;
	      reader.onload = function() {
	        chatContentRef.innerHTML +=
	      `<div class="clearfix"><div style="float:right"> <span style="margin-right:8px;">我</span>${time}</div> </div>
	      <div class="clearfix">
	        <div style="background-color:#5595FF;color:#fff;padding:8px 8px;text-align:rigth;margin-top:8px;margin-left:52px;border-radius:6px;
	        float:right"> <img src=${reader.result} style="max-width:100px;"></img> </div>
	      </div>
	      <br />`;
	        const innerHTML = ` <img src=${reader.result} style="max-width:100px;"></img>`;
	        var msg = {
	          'text': innerHTML,
	          'userName': users
	        };
	        stompClient.send('/sendUser', {}, JSON.stringify(msg));
	      };
	    },
	    closeChatCheck() { //断开链接
	      this.chatDialog = false;
	      this.closeChatCheckDialog = false;
	      this.disconnect();
    	}
    },
    mounted(){
        this.init();
    }
}

以上就是websocket的通信内容。。。

······························································我是一条分割线····························································
以下是页面效果图:
1.客服页面:
在这里插入图片描述
2.客户页面:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值