Echarts如何优雅加载十万量级的数据

1,526 阅读6分钟

Echarts作为一个强大的开源可视化库,在日常开发中,广泛应用于数据可视化、数据分析、信息展示和数据报告等多种场景,然而,当数据量达到十万甚至更多时,图表的加载和渲染效率成为挑战。本文将介绍如何使用 ECharts 的数据分段加载和降采样策略来优化大数据图表的显示,以提升性能和用户体验。

(一)正常加载十万条数据:

如下图所示:

  1. 首先我们先看下这十万条数据加载页面的情况,海量数据已经连成一片,都不知道加载的是什么东西了。
  2. 其次我们再看下这十万条数据的性能,总加载时间用了12s,绘制时间用了4s, 如果等12s去看这个图表,这对于用户体验来说必然是不可接受的。

9999.png (二)优化方案:

遇到上述的渲染瓶颈,我们将使用数据分段加载(DataZoom)和降采样策略(Sampling)来优化大数据图表的显示,以提升性能和用户体验。

方案一:Datazoom(数据分段加载):主要是用于分区域渲染,然后自然的关注数据的细节,或者浏览整体,他提供了几个属性可以控制这个图标渲染时的性能问题,比如start数据范围起始百分比,比如end范围结束的百分比

dataZoom: [{
      type: 'slider', // 值:inside slider
      start: 0, // 数据窗口范围的起始百分比
      end: 100, // 数据窗口范围的结束百分比
      realtime: true, // 是否实时更新 true/false
      xAxisIndex: 0, // 表示特定的X轴索引
      minSpan: 0, // 用于限制窗口大小的最小值(百分比值),取值范围是 0 ~ 100。
      maxSpan: 100, // // 用于限制窗口大小的最大值(百分比值),取值范围是 0 ~ 100。
      // 特别提醒:start: 设置为0, 说明是从第1条数据开始的; 那么xAxisIndex就必须是0; 因为xAxisIndex不是0,他们就互相矛盾了。
      filterMode: 'filter'
}, {
      type: 'slider',
      start: 0,
      end: 100,
      realtime: true,
      yAxisIndex: 0, // 表示特定的y轴索引
      minSpan: 0,
      maxSpan: 100,
      filterMode: 'empty'
}]

加入优化方案后,再来感受一下渲染性能:

  1. 很明显页面图表渲染流畅很多,因为他只加载了区域部分数据。
  2. 执行时间从12秒降到1.3秒,绘制时间从4s降到0.3s,页面性能有了大幅提升。

999.png

方案二:Sampling(降采样策略): 减少数据点的数量,减少渲染负担,以提高性能或提供更清晰的视觉表现。降采样可以通过多种方式实现,例如简单地跳过一些数据点,或使用算法如平均值、最大值、最小值等来聚合数据点。

series: [{
      name: '销量',
      data: sampledData,
      type: 'bar',
      sampling: 'lttb',
      // sampling的值:
      // 'lttb' 采用 Largest-Triangle-Three-Bucket 算法,可以最大程度保证采样后线条的趋势,形状和极值。
      // 'average' 取过滤点的平均值
      // 'min' 取过滤点的最小值
      // 'max' 取过滤点的最大值
      // 'minmax' 取过滤点绝对值的最大极值 (从 v5.5.0 开始支持)
      // 'sum' 取过滤点的和
}, {
      name: '金额',
      data: sampledData,
      type: 'line'
}],

看效果:

  1. 这个渲染效果是保留了原图表渲染的趋势,原本什么样,这个渲染出来的这个趋势图和原来差不多,但他是做了过滤的。
  2. 执行时间从12秒降到1.3秒,绘制时间从4s降到0.3s,明显也不卡顿了。

999.png (三)总结上述两种优化方案的优缺点

DataZoom(数据分段加载)
优点:
  1. 配置简单:DataZoom 的配置相对简单,通过设置 start 和 end 属性,可以控制图表渲染的数据范围,从而提高性能。
  2. 流畅渲染:使用 DataZoom 后,页面渲染速度非常快,能够实现快速渲染大量数据而不影响用户体验。
缺点:
  1. 局部数据展示:DataZoom 只能查看指定局部的数据,无法查看整体数据,这可能限制了用户对数据整体趋势的把握。
Sampling(降采样策略)
优点:
  1. 整体趋势可见:与 DataZoom 不同,Sampling 策略允许用户看到数据的整体趋势,而不是仅限于局部数据。
  2. 减少数据点:通过减少数据点的数量,Sampling 策略可以减轻渲染负担,提高性能,尤其是在数据点非常多的折线图中效果明显。
缺点:
  1. 数据丢失:Sampling 策略可能会导致部分数据丢失,这对于需要精确数据的用户来说可能是一个问题。
  2. tooltip功能可能受限:由于数据点的减少,一些图表的交互功能,如 tooltip,可能无法实现或受到影响。

综上所述,DataZoom 和 Sampling 是处理 ECharts 大数据渲染的两种有效策略,它们各有优势和局限。DataZoom 适合于需要快速渲染大量数据的场景,而 Sampling 更适用于需要观察数据整体趋势的情况。在实际应用中,可以根据具体需求选择合适的策略来优化图表的显示和性能。

(四)上完整代码

新建ChartComponent.vue子组件

<template>
  <div ref="chart" style="width: 100%; height: 400px;"></div>
</template>

<script>
import * as echarts from 'echarts'

export default {
  name: 'ChartComponent',
  props: {
    echartsdata: Array // 假设传入的数据是数组
  },
  mounted () {
    this.initChart()
  },
  watch: {
    echartsdata (newval, oldval) {
      this.initChart()
    }
  },
  methods: {
    initChart () {
      const chart = echarts.init(this.$refs.chart)
      const sampledData = this.echartsdata
      const option = {
        // ECharts 配置项
        xAxis: {
          type: 'category'
          // data: Array.from({ length: sampledData.length }, (_, i) => i)
        },
        yAxis: {
          type: 'value'
        },
        series: [{
          name: '销量',
          data: sampledData,
          type: 'bar',
          sampling: 'min',
          // 'lttb' 采用 Largest-Triangle-Three-Bucket 算法,可以最大程度保证采样后线条的趋势,形状和极值。
          // 'average' 取过滤点的平均值
          // 'min' 取过滤点的最小值
          // 'max' 取过滤点的最大值
          // 'minmax' 取过滤点绝对值的最大极值 (从 v5.5.0 开始支持)
          // 'sum' 取过滤点的和
        }, {
          name: '金额',
          data: sampledData,
          type: 'line'
          // sampling: 'lttb', // 最大程度保证采样后的线条趋势。
          // sampleSize: 10
        }],
        dataZoom: [{
          type: 'slider', // 值:inside slider
          start: 0, // 数据窗口范围的起始百分比
          end: 100, // 数据窗口范围的结束百分比
          realtime: true, // 是否实时更新 true/false
          xAxisIndex: 0, // 表示特定的X轴索引
          minSpan: 0, // 用于限制窗口大小的最小值(百分比值),取值范围是 0 ~ 100。
          maxSpan: 100, // // 用于限制窗口大小的最大值(百分比值),取值范围是 0 ~ 100。
          // 特别提醒:start: 设置为0, 说明是从第1条数据开始的; 那么xAxisIndex就必须是0; 因为xAxisIndex不是0,他们就互相矛盾了。
          filterMode: 'filter'
        }, {
          type: 'slider',
          start: 0,
          end: 100,
          realtime: true,
          yAxisIndex: 0, // 表示特定的y轴索引
          minSpan: 0,
          maxSpan: 100,
          filterMode: 'empty'
        }]
      }
      chart.setOption(option)
    }
  }
}
</script>

父组件:

<template>
  <div>
    <button @click="loadinglargeData">加载大数据</button>
    <chart-component :echartsdata="largeData" :sample-rate="6"></chart-component>
    {{largeData}}
  </div>
</template>

<script>
import ChartComponent from './components/ChartComponent.vue'

export default {
  components: {
    ChartComponent
  },
  data () {
    return {
      largeData: [950, 230, 12, 618, 35, 147, 260, 1100, 90, 510, 20, 0, 1018, 135, 947, 760, 100, 90, 150]
    }
  },
  created () {
  },
  methods: {
    // 点击加载数据
    loadinglargeData () {
      this.createLargeData()
    },
    // 随机生成100000条数据
    createLargeData () {
      let largeData = []
      for (let i = 0; i < 3000; i++) {
        largeData.push(Math.floor(Math.random() * 1000))
      }
      console.log(largeData)
      this.largeData = largeData
    }
  }
}
</script>