【前端 webSocket】js vue两种基本使用

247 阅读3分钟

我正在参加「码上掘金挑战赛」详情请看:码上掘金挑战赛来了!

前言

websocket是什么呢?官方的解释是浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。

我的理解是搭建一个前后端的桥梁,可以实时发送消息。例如我登录了网站以后,有什么新的新闻动态立马给我弹窗提示。不能总是前端循环调接口看看后端有没有新闻吧。

最近我也是做到一个WebSocket相关的需求,也记录一下。

一、创建ws对象

var ws = new WebSocket(url, [protocol] );

  • url:访问后端的地址,ws或者wss开头,如:ws://localhost:3000/websocket。(WSS(Web Socket Secure)是WebSocket的加密版本)
  • protocol:需要放到headers中传给后端的参数,如需要验证身份的token。可传多个,逗号隔开。

二、几种状态

readyState属性值对应的状态
0正在建立连接连接,还没有完成
1连接成功建立,可以进行通信
2连接正在进行关闭握手,即将关闭
3连接已经关闭或者根本没有建立

三、使用WebSocket

普通的js简单调用

var ws = new WebSocket("ws://localhost:3000/websocket");//连接服务器
ws.onopen = function(e){alert("已经与服务器建立了连接。当前连接状态:"+readyState);};
ws.onmessage = function(res){alert("接收到服务器发送的数据:"+res.data);};
ws.onclose = function(e){alert("已经与服务器断开连接");};
ws.onerror = function(e){alert("WebSocket连接失败!");};

普通的vue简单调用

  • 只演示简单例子,如果需要多次调用可以封装起来使用
<template>
    <div>
      websocket示例
    </div>
  </template>
  <script>
    export default {
      name: 'Websocket',
      data() {
        return {
          url: ws://localhost:3000/websocket', // ws wss
          socket: null
        }
      },
      mounted() {
        this.initWebSocket()
      },
      methods: {
        reconnect() {
          console.log('尝试重连')
          setTimeout(() => 
            this.initWebSocket()
          }, 60 * 1000)// 加个延时
        },
        initWebSocket() {
          try {
            if ('WebSocket' in window) {
              this.socket = new WebSocket(this.url)
            } else {
              console.log('您的浏览器不支持websocket')
            }
            this.socket.onopen = this.websocketonopen
            this.socket.onerror = this.websocketonerror
            this.socket.onmessage = this.websocketonmessage
            this.socket.onclose = this.websocketclose
          } catch (e) {
            this.reconnect()
          }
        },
        websocketonopen() {
          console.log('WebSocket连接成功', this.socket.readyState)
        },
        websocketonerror(e) {
          console.log('WebSocket连接发生错误', e)
          this.reconnect()
        },
        websocketonmessage(res) {
          let data = JSON.parse(res.data)
          console.log('响应数据:', data)
        },
        websocketclose(e) {
          console.log('连接中断了' + e )
        }
      },
      destroyed() {
        this.socket.close()
      }
    }
  </script>

配置代理

  • 在vue.config.js里配置websocket的代理
module.exports = {
    devServer: {
        proxy: {
            '/ws': { // websocket代理
                target: '代理目标的基础路径', 
                changeOrigin: true,
                ws:true // 开启websocket代理
            }
            // ...其他代理
        }
    }
}


Nginx配置

此处不是我配置的,我就不写啦

websocket心跳机制

(1)为什么增加websocket心跳?

  • 在使用websocket的过程中,有时候会遇到网络断开的情况,但是在网络断开的时候服务器端并没有触发onclose的事件。服务器会继续向客户端发送多余的链接,并且这些数据还会丢失。

  • 这个问题被发现是因为有次执行,等待时间特别长,我们的使用nginx代理,nginx配置了访问超时时间。在测试环境就有问题了,在我本地没有问题。

  • 所以需要加上websocket的心跳,还有心跳,说明还活着,没有心跳说明已经关闭了。

  • 因为我这个业务,有两个页面需要用到websocket,所以把它封装到了一个js文件

(2)websocket心跳原理

  • 创建一个定时器,每隔一段时间就send一次信息,这样Nginx就不会自己断websocket了(发送心跳)
  • 如果收到后台返的消息,就重置定时器,说明心跳没有断(重置心跳)
  • 后端无需返回相应的心跳也没有问题。
  • 完整代码如下:
class BaseWebSocket {
    constructor(options) {
        this.ws = null; 
        this.url = options.url;
        this.status = null; 
        this.isHeart = options.isHeart; // 是否开启心跳
        this._timeout = 30000; // 每隔时间发送心跳
        this.msgCallback = options.msgCallback;  // 收到消息的回调
        this.pingInterval = null; // 定时器
        this.lockReconnect = false;
        this.isReconnec = options.isReconnec; // 是否重连
        this.connect();
    }

    connect() {
        if (this.lockReconnect) {
            return;
        }
        this.lockReconnect = true;
        this.createWebSocket();
        this.lockReconnect = false;
    }

    createWebSocket() {
        if ('WebSocket' in window) {
            this.ws = new WebSocket(this.url);
        } else if ('MozWebSocket' in window) {
            this.ws = new MozWebSocket(this.url);   
        } else {
            console.log('您的浏览器不支持websocket协议,建议使用新版谷歌、火狐等浏览器,请勿使用IE10以下浏览器')
        }
        this.initEvent();
    }

    initEvent() {
        this.ws.onopen = (e) => {
            this.status = 'open';
            if(this.isHeart) {
                this._heartCheck()
            }
        }

        this.ws.onmessage = (e) => {
            if(typeof this.msgCallback === 'function'){
                return this.msgCallback(e.data)
            }else{
                console.log('no function')
            }
        }

        this.ws.onclose = (e) => {
            this._close(e)
        }
        this.onerror = (e) => {
            this._close(e)
        }
        
    }

    _resetHeart() {
        clearInterval(this.pingInterval)
        return this
    }

    _heartCheck() {
        this.pingInterval = setInterval(() => {
            if(this.ws.readyState === 1) {
                this.ws.send(JSON.stringify({type: 'ping'}))
            }
        }, this._timeout)
    }

    _close(e) {
        this._resetHeart()
        if(this.status !== 'close') {
            if(this.isReconnec){
                this.connect()
            }
        }else{
            console.log(e)
        }
    }

    close() {
        this.status = 'close'
        this._resetHeart()
        return this.ws.close();
    }

    send(data) {
        return this.ws.send(JSON.stringify(data))
    }
}

  • 调用
import BaseWebSocket from '...'


const ws = new webSocket({
    url: 'ws://...',
    isHeart: true,
    isReconnec: true,
    msgCallback:this.msgCallback
});
    
    
msgCallback(data){
    // 处理收到消息的业务
}