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', width: '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 === '\n') { 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>

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM