vue-cli使用sockjs即时通信

  基于webSocket通信的库主要有 socket.ioSockJS,这次用的是 SockJS。

  这里我们使用sockjs-clientstomjs这两个模块,要实现webSocket通信,需要后台配合,也使用相应的模块。

1、sockjs-client

  sockjs-client是从SockJS中分离出来的用于客户端使用的通信模块,所以我们就直接来看看SockJS。SockJS是一个浏览器的JavaScript库,它提供了一个类似于网络的对象,SockJS提供了一个连贯的、跨浏览器的JavaScriptAPI,它在浏览器和Web服务器之间创建了一个低延迟、全双工、跨域通信通道。你可能会问,我为什么不直接用原生的WebSocket而要使用SockJS呢?这得益于SockJS的一大特性,一些浏览器中缺少对WebSocket的支持,因此回退选项是必要的,而Spring框架提供了基于SockJS协议的透明的回退选项。SockJS提供了浏览器兼容性,优先使用原生的WebSocket,如果某个浏览器不支持WebSocket,SockJS会自动降级为轮询。

2、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之上提供了一个基于帧的线路格式层,用来定义消息语义.

3、代码实现:

  先安装 sockjs-client 和 stompjs

npm install sockjs-client
npm install stompjs

  简单代码:

<script>
  import SockJS from 'sockjs-client'
  import Stomp from 'stompjs'
  import { getLiveUrlApi, getLiveEventDetailApi } from '@/apis'
  import { mapGetters } from 'vuex'
  import { picklist, isWinxin, isMobile, initWxShare } from '@/utils'
  import Emoji from './emoji'
  export default {
    data () {
      return {
        stompClient: '',
        timer: '',
        player: null,
        pkl: picklist,
        danmukuList: [],
        barrageInfo: {
          text: ''
        },
        total: 0,
        eventInfo: {
          endTime: '',
          speakers: [{}]
        },
        emojiShow: false,
        noticeShow: false,
        sharehref: '',
        isWeixinBrowser: isWinxin(),
        isMobileBrowser: isMobile()
      }
    },
    computed: {
      ...mapGetters(['token', 'userInfo', 'isSys', 'permissions']),
      isAdmin () {
        // sys、活动管理员、已报名且在直播的普通用户
        return this.isSys || (this.eventInfo.ticketStatus === 2 && this.eventInfo.liveStatus === 1) || this.permissions.includes('event')
      }
    },
    components: {
      Emoji
    },
    methods: {
      getEvent () {
        getLiveEventDetailApi(this.$route.params.id).then(res => {
          if (res.status === 200) {
            this.eventInfo = res.data
            if (this.isWeixinBrowser) {
              this.initShare()
            }
            if (this.isAdmin) {
              this.fetchData()
            }
          }
        })
      },
      fetchData () {
        getLiveUrlApi(this.$route.params.id).then(res => {
          if (res.status === 200) {
            this.aliPlay(res.data)
          }
        })
      },
      // 阿里云视频直播
      aliPlay (source) {
        let _videoDom = document.getElementById('aliVedio')
        let _ch = _videoDom.clientHeight / 2 - 32
        let _cw = _videoDom.clientWidth / 2 - 32
        this.player = new Aliplayer({
          id: 'aliVedio',
           '100%',
          source: source,
          autoplay: true,
          isLive: true,
          rePlay: false,
          playsinline: true,
          preload: true,
          controlBarVisibility: 'hover',
          useH5Prism: true,
          enableStashBufferForFlv: false,
          stashInitialSizeForFlv: 32,
          skinLayout: [
            {
              'name': 'bigPlayButton',
              'align': 'blabs',
              'x': _cw,
              'y': _ch
            },
            {
              'name': 'controlBar',
              'align': 'blabs',
              'x': 0,
              'y': 0,
              'children': [
                {
                  'name': 'liveDisplay',
                  'align': 'tlabs',
                  'x': 15,
                  'y': 6
                },
                {
                  'name': 'fullScreenButton',
                  'align': 'tr',
                  'x': 10,
                  'y': 14
                },
                {
                  'name': 'volume',
                  'align': 'tr',
                  'x': 5,
                  'y': 13
                }
              ]
            }
          ],
          components: [{
            name: 'AliplayerDanmuComponent',
            type: AliPlayerComponent.AliplayerDanmuComponent,
            args: [this.danmukuList]
          }]
        }, function (player) {
          player._switchLevel = 0
        })
      },
      // 连接后台
      connection () {
        let that = this
        // 建立连接对象
        let sockUrl = '/api/event-websocket?token=' + this.token.substring(7) + '&eventId=' + this.$route.params.id
        let socket = new SockJS(sockUrl)
        // 获取STOMP子协议的客户端对象
        this.stompClient = Stomp.over(socket)
        // 定义客户端的认证信息,按需求配置
        let headers = {
          Authorization: ''
        }
        // 向服务器发起websocket连接
        this.stompClient.connect(headers, (res) => {
          // 订阅服务端提供的某个topic
          this.stompClient.subscribe('/topic/event/' + this.$route.params.id, (frame) => {
            that.addBarage(JSON.parse(frame.body))
          })
          that.sentFirst()
        }, (err) => {
          console.log('失败:' + err)
        })
        this.stompClient.debug = null
      },
      // 断开连接
      disconnect () {
        if (this.stompClient) {
          this.stompClient.disconnect()
        }
      },
      // 初始化websocket
      initWebSocket () {
        this.connection()
        let that = this
        // 断开重连机制,尝试发送消息,捕获异常发生时重连
        this.timer = setInterval(() => {
          try {
            that.stompClient.send('connect-test')
          } catch (err) {
            console.log('断线了: ' + err)
            that.connection()
          }
        }, 5000)
      },
      // 用户加入发送弹幕
      keyDown (e) {
        if (e.keyCode === 13) {
          if (e.preventDefault) {
            e.preventDefault()
          } else {
            window.event.returnValue = false
          }
          this.sentBarrage()
        }
      },
      sentBarrage () {
        this.emojiShow = false
        if (this.barrageInfo.text === '' || this.barrageInfo.text === '
') {
          this.barrageInfo.text = ''
          return false
        }
        let that = this
        this.barrageInfo.eventId = this.eventInfo.id
        this.barrageInfo.name = this.userInfo.account
        this.barrageInfo.role = this.userInfo.roleName
        if (this.permissions.length > 0) {
          this.barrageInfo.role = 'admin'
        }
        this.barrageInfo.headimgurl = this.userInfo.headimgurl
        let reg = /#[u4E00-u9FA5]{1,4};/gi
        this.barrageInfo.comment = this.barrageInfo.text
        if (reg.test(this.barrageInfo.text)) {
          this.barrageInfo.text = this.barrageInfo.text.replace(reg, '')
        }
        this.stompClient.send(
          '/msg',
          {},
          JSON.stringify(that.barrageInfo)
        )
        this.barrageInfo.text = ''
      },
      // 连接建立,首次发送消息
      sentFirst () {
        let that = this
        this.barrageInfo.eventId = this.eventInfo.id
        this.barrageInfo.name = this.userInfo.account
        this.barrageInfo.role = this.userInfo.roleName
        if (this.permissions.length > 0) {
          this.barrageInfo.role = 'admin'
        }
        this.barrageInfo.headimgurl = this.userInfo.headimgurl
        this.stompClient.send(
          '/msg',
          {},
          JSON.stringify(that.barrageInfo)
        )
      },
      // 添加弹幕内容
      addBarage (content) {
        // 推送直播状态修改页面
        if (content.live) {
          if (this.isSys || this.permissions.includes('event')) {
            return
          }
          this.getEvent()
        }
        if (content.onlineCount) {
          this.total = content.onlineCount
        }
        let _obj = {
          'mode': 1,
          'stime': 1000
        }
        content = Object.assign(_obj, content)
        this.danmukuList.push(content)
        this.$nextTick(() => {
          let barrage = document.getElementById('barrage')
          barrage.scrollTop = barrage.scrollHeight
        })
      },
      // 将匹配结果替换表情图片
      emotion (res) {
        let word = res.replace(/#|;/gi, '')
        const list = [
          '微笑', '咧嘴笑', '破涕为笑', '', '眯眼', '', '害羞', '放电',
          '亲亲', '得意', '惊恐', '眼泪', '', '', '吐舌', '害羞笑',
          '', '同意', '拍手', '鼓掌', '红唇', '', '夜晚', '少儿不宜',
          '强壮', '', '太阳', '香蕉', '举杯', '国旗', '心动', '奶牛',
          '恶魔', '礼物', '鸡腿', '玫瑰', '西红柿', '茄子', '西瓜', '草莓'
        ]
        let index = list.indexOf(word)
        return `<img src="https://cdn.enmotech.com/attach/twemoji/${index}.png" align="middle" width="28px">`
      },
      setEmoji (arg) {
        this.barrageInfo.text += arg
      },
      // 微信分享
      initShare () {
        let _obj = {
          title: '活动直播:' + this.eventInfo.title,
          img: 'https://cs.enmotech.com/image/event/event_1556264441839.png',
          desc: '我正在观看' + this.eventInfo.speakers[0].name + '的直播:《' + this.eventInfo.title + '》,快来围观吧!'
        }
        initWxShare(_obj)
      }
    },
    mounted () {
      this.sharehref = location.href
      this.getEvent()
      this.initWebSocket()
    },
    beforeDestroy () {
      if (this.player) {
        this.player.dispose()
        this.player = null
      }
      // 页面离开时断开连接,清除定时器
      this.disconnect()
      clearInterval(this.timer)
    }
  }
</script>
原文地址:https://www.cnblogs.com/goloving/p/10686826.html