用Echarts + Websocket 实现页面图表实时更新

12,106 阅读3分钟
需求阶段

最近遇到一个需求--“在页面实时展示后台服务器及网络设备的运行状态”

技术选型

整个项目是基于Vue
图表的话我选用Echarts这个库
实时更新?那我们要用到什么技术呢?
这里肯定不能再使用Http协议,Http的生命周期通过Request来界定,也就是Request一个Response,客户端发请求,服务器响应请求,服务器不能主动向客户端发请求,并且这个连接是短暂的。
所以这时候就要用到Websocket

Websocket是什么

WebSocket是基于TCP的应用层协议,用于在C/S架构的应用中实现双向通信,虽然WebSocket协议在建立连接时会使用HTTP协议,但这并不意味着WebSocket协议是基于HTTP协议实现的。
客户端可以通过Websocket与服务器建立长连接,通过这个可以实现页面的实时更新。

代码阶段
  • 在项目中下载Echarts并引入 import * as echarts from "echarts";(此为引入到页面的)
  • 在页面上使用Echarts图表(散点图为例)
export default {
  data() {
    return {
      dataArr: [],
      mychart: null,
      mapFunc: new Map(),
    };
  },
  methods: {
    chart1() {
      var chartDom = document.getElementById("main");
      this.myChart = echarts.init(chartDom);
      var option;
      option = {
        xAxis: {},
        yAxis: {},
        series: [
          {
            symbolSize: 20,
            data: this.dataArr,
            type: "scatter",
          },
        ],
      };
      option && this.myChart.setOption(option);
       },
      }
    }

此时需要渲染的数据dataArr为空

  • 用Websocket从后端实时获取数据
 getWebSocket() {
      //建立websocket连接
      const ws = new WebSocket("ws://192.168.31.171:8090/api/echarts/ws");
     //连接成功后触发
      ws.onopen = (e) => {
        console.log('已连接');
      };
      //接收消息时触发
      ws.onmessage = (msg) => {
      //接收到消息后对接收到的数据进行处理,处理成图表需要的结构
        let n = msg.data.split(",");
        const item = [Number(n[1]), Number(n[2])];
        this.dataArr.push(item);
        //触发图表更新
        this.chart1()
      };
      //连接断开时触发
       ws.onclose = (event)=> {
       console.log("websocket connection close.");
       console.log(event.code); 
       };
       //连接出错时触发
       ws.onerror = (event)=> { 
       console.log("websocket connection error."); 
       console.log(event); 
       };
    },

定义好之后对方法在mounted中进行调用

mounted() {
//先让图表显示了,再进行websocket连接
    this.chart1();
    this.getWebSocket();
  },

这时候已经实现了单个图表的实时更新

注意⚠

注意一

这时页面上可能会有一个警告--说页面上已经有Echarts的实例了,这个问题在哪里呢?
当我们收到websocket信息时,在最后调用了chart1函数去触发图表更新,在chart1中,

      var chartDom = document.getElementById("main");
      this.myChart = echarts.init(chartDom);

这两行代码在函数调用时会被重复执行,所以报了这个警告
为了解决这个警告,我将这两行代码移至mounted钩子函数内

mounted() {
      var chartDom = document.getElementById("main");
      this.myChart = echarts.init(chartDom);
//先让图表显示了,再进行websocket连接
    this.chart1();
    this.getWebSocket();
  },

此时就没有了那个警告~

注意二

当前代码只展示了单个图表的更新,如果有多个图表呢?
这个就和后端返回的数据结构有关系了
后端一般返回的数据会带有一个属性,用于定位是那个图表的数据
这时候前端拿到数据需要先判断是哪一个图表的,再更新目标图表
这有很多种做法,比如进行if判断,完全可以解决问题,只是性能上就不那么友好了。
我的解决方法是:用一个Map结构的数据来存储 图表名称 与 图表更新方法 的映射关系 拿到数据时直接调用对应的方法就行了
为什么用Map呢,因为Map的底层是二叉树~查找起来非常快,也很节约性能

结束

这里的代码只是一个小小的demo,只提供思路和方法,还有很大的优化空间,这里就不具体说啦~谢谢大家~