记录一次echarts图表大数据量轮询刷新页面卡死问题的优化

项目场景:

在我们的项目架构中,集成的Echarts图表组件采用了折线图,业务需求即每300毫秒自动更新图表上的数据,并且每一次的数据点数量达到了约700个,折线图刷新的很快,每300毫秒就要刷新数据

问题描述

开发过程中发现在这种数据量请求频率下,大概2个小时左右就会导致整个页面卡死,无法操作。问题很严重

原因分析:

经过详细的排查发现是echarts图表在大数据量下确实会有这种问题。我们业务虽然只有两个图标,但更新频率很快,也遇到了此问题。原因是多方面的,一个是配置不高的电脑cpu撑不住,一个echarts也有内存问题,在大数据量频繁刷新内存和cpu撑不住,直接导致页面崩溃。经过考虑采取先不降低频率,先优化图表方式。解决这个问题需要从多方面下手

解决方案:

1.解决定时轮询稳定性:

将定时轮询单独放在一个线程中,稳定定时任务运行效率
参考代码:
// worker.js   
self.addEventListener("message", function (event) {
  const { task } = event.data;
  console.log("worker", task);
  if (task === "getChartData") {
  	//创建定时任务
    if (!self.timerThread) {
      self.timerThread = this.setInterval(async () => {
        self.postMessage({});
      }, 300);
    }
  }else if (task === "stop") {
    //清除定时任务
    if (self.timerThread) {
      this.clearInterval(self.timerThread);
      self.timerThread = undefined;
    }
  }
});

调用

    stopComputation(){
      if (this.worker != null) {
        this.worker.postMessage({ task: 'stop'});
        this.worker.terminate();
        this.worker = null;
      }
    },
	startTimer(){
      if (this.worker != null) {
        this.stopComputation()
      }
      this.worker = new Worker(new URL('./chartthread.js', import.meta.url));
      this.worker.postMessage({ task: 'getChartData'});
      this.worker.onmessage = async () => {
        console.log('Worker has been getVibration...............................');
        this.getVibration()
      };
      // 清理 Worker
      this.worker.onclose = function() {
        console.log('Worker has been terminated.');
      };

修改之后发现发现有效果,但是效果不明显,最后还是卡死

2.将echarts实例单独提取到vue外部,避免vue多余监听浪费性能

具体代码
<script>
import elementResizeDetectorMaker from 'element-resize-detector' // 尺寸监听组件
//设置此处会被多个vue组件公用,所以用key,value方式保存各个组件页面的实例,避免echarts实例公用
let echartObject = {}
export default {
 	.......此处省略部分代码........
  data () {
    return {
      options: {},
      chartInstanceIndex: null, //图表实例
      timer: null
    }
  },
  mounted () {
    //随机8位数
    this.chartInstanceIndex = Math.random().toString(36)
    this.timer = setInterval(() => {
      this.initChart()
    }, 60000)
    this.initChart()
    const erd = elementResizeDetectorMaker()
      // 为了健壮性,可以考虑一下这里获取dom元素进行一个判断,如果没有获取到dom元素就不进行监听,这里就这样了
      erd.listenTo(document.getElementById(this.id), (element)=> {
        if (echartObject[this.chartInstanceIndex] != null && echartObject[this.chartInstanceIndex] != undefined) {
          echartObject[this.chartInstanceIndex].resize()
        }
      })
  },
  methods: {
    initChart() {
      if(!document.getElementById(this.id)){
        return
      } 
      if (echartObject[this.chartInstanceIndex] != null && echartObject[this.chartInstanceIndex] != undefined) {
        echartObject[this.chartInstanceIndex].dispose()
        echartObject[this.chartInstanceIndex] == null
      }
      echartObject[this.chartInstanceIndex] = this.$echarts.init(document.getElementById(this.id))
      this.options = {...省略....}
      echartObject[this.chartInstanceIndex].setOption(this.options)
    },

    updateData () {
      if(echartObject[this.chartInstanceIndex] != null){
        echartObject[this.chartInstanceIndex].setOption({
          series: [{
            data: this.yData
          }],
        })
      }
    }
  }
}
</script>

3.关闭动画可提高性能

官网配置项
options.animation改成false

4.配置折线图在数据量远大于像素点时候的降采样策略

官方文档:官方文档
在这里插入图片描述
有几种采样算法,此处我才用了第一种lttb算法。经过前面几个方法优化效果明显,但经过大约4个小时后还是撑不住,最终使用了下面的方法成功解决问题

5.定时删除并重新创建echarts实例

如果试了上面的方法还是不行。那么最后一种方式可以试一下。原理是定时重新创建并清除旧的实例,相当于重新初始化一次
代码参考:

<script>
import elementResizeDetectorMaker from 'element-resize-detector' // 尺寸监听组件
let echartObject = {}
export default {
  ...............................
  data () {
    return {
      options: {},
      chartInstanceIndex: null, //图表实例
      timer: null
    }
  },
  destroyed() {
    if (this.timer!=null) {
      clearInterval(this.timer)
      this.timer == null
    }
  },
  mounted () {
    //随机8位数
    this.chartInstanceIndex = Math.random().toString(36)
    this.timer = setInterval(() => {
    	//定时重新初始化
      this.initChart()
    }, 60000)
    this.initChart()
...........................
  },
  ..............
  methods: {
    initChart() {
      if(!document.getElementById(this.id)){
        return
      } 
      if (echartObject[this.chartInstanceIndex] != null && echartObject[this.chartInstanceIndex] != undefined) {
        echartObject[this.chartInstanceIndex].dispose()
        echartObject[this.chartInstanceIndex] == null
      }
      echartObject[this.chartInstanceIndex] = this.$echarts.init(document.getElementById(this.id))
      this.options = {........................}
      echartObject[this.chartInstanceIndex].setOption(this.options)
    },

    updateData () {
      if(echartObject[this.chartInstanceIndex] != null){
        echartObject[this.chartInstanceIndex].setOption({
          series: [{
            data: this.yData
          }],
        })
      }
    }
  }
}
</script>

经过该方式优化后成功解决了长时间运行页面卡死的问题

总结

经过上面几个方法的优化,在没有降低请求频率的前提下成功解决页面卡死的问题。以上代码仅供参考
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值