数据条数过多时,加载缓慢,从获取到数据、处理数据、绘制图表渲染出来;
按照条件把这些大数据分为几个图表,同时绘制也是会存在卡顿。
就这几个过程,但是中间经过的点太多了,累积起来,都是时间、性能的考验和消耗。
AI其实把基本的性能优化以后,其他都靠时间日志对每一个步骤进行录制,再进行分析修改。

排查的时候就发现其实Echarts本身会有一些对于性能上的支持,虽然说有些时候会牺牲一些优美的样式。
我的项目里用了各式各样的symbol,这个开销可太大了,数据一多,使用symbol的时间比起不适用的时间成倍增长。
而且为了美观,tooltip里的图例直接使用了svg绘制,开销更大。

把更改的点进行了记录,也是一个警戒,后续再有类似的图表和数据量也可以直接就开始从部分点去约束。

一、数据层:减少进入图表的数据量

手段说明建议阈值
服务端降采样接口返回前按时间窗口聚合,只返回前端需要的粒度单条 series > 5000 点时
前端降采样算法LTTB 保留视觉特征(波峰波谷),优于等间隔采样单条 > 1000 点时
等间隔采样最简单,按固定步长取点,适合平缓曲线精度要求不高时
按需加载初始只加载概览数据,缩放/拖拽时再请求细节时间跨度大时

LTTB 核心思路:将数据分桶,每桶选取与前一点和下一桶平均点构成最大三角形面积的点,比等间隔采样更好保留曲线形态。

二、ECharts 配置层:关闭不必要的功能

{
  animation: false,           // 关闭入场动画
  hoverAnimation: false,      // 关闭 hover 放大
  silent: false,              // 需要交互时不用 silent:true
  sampling: 'lttb',           // 启用内置采样(作为兜底)
  progressive: true,          // 渐进渲染
  progressiveThreshold: 2000, // 超过此值启用
}

原则:每一项功能(动画、阴影、渐变、hover 效果)都有渲染成本,大数据量下能关则关。

三、Series 级配置

{
  large: true,              // 大数据模式,简化路径
  largeThreshold: 500,      // 超过此值启用 large
  showSymbol: false,        // 关闭标记点(最大性能提升点)
  showAllSymbol: false,     // 即使开 symbol 也只显示首尾
  symbol: 'none',           // 数据点多时直接设 none
  symbolSize: 4,            // 必须显示时用小尺寸
  hoverAnimation: false,
  emphasis: { disabled: true }, // 关闭高亮
  lineStyle: { width: 1 },     // 线宽收窄
  smooth: false,               // 关闭平滑曲线
  step: false,                 // 关闭阶梯
}

关键showSymbol 是性能影响最大的单项配置。标记点在每个数据点渲染一个 SVG 元素,数据量大时开销是纯线段的 5-10 倍。

四、Symbol 专项优化策略

是否需要显示标记点?
  │
  ├─ 不需要 → showSymbol: false(最优)
  │
  └─ 需要 → 分级处理
       │
       ├─ 数据点 > 80 → symbol: 'none'(自动隐藏)
       │
       ├─ 数据点 > 500 → showAllSymbol: false + large: true
       │
       └─ 数据点 < 80 → 正常显示,但关闭 hoverAnimation

如果必须显示 symbol,配合更激进的降采样(比无 symbol 时再砍 40% 数据点)。

五、setOption 调用优化

// notMerge 模式:跳过内部 diff,直接替换
chart.setOption(option, true)

// 而非默认的 merge 模式
chart.setOption(option) // 会做深层 diff,大数据量时很慢

适用场景:全量数据更新、切换数据源、切换显示模式时用 notMerge=true;局部更新(如只改颜色)用默认 merge。

六、渐进式渲染

// series 数量多时(> 100),分批渲染
async function progressiveRender(chart, allSeries, batchSize = 50) {
  for (let i = 0; i < allSeries.length; i += batchSize) {
    const batch = allSeries.slice(i, i + batchSize)
    if (i === 0) {
      chart.setOption({ series: batch })
    } else {
      chart.appendData({ seriesIndex: i }, batch)
    }
    await new Promise(r => setTimeout(r, 0)) // 让出主线程
  }
}

原理:首批数据快速呈现给用户,后续批次在事件循环空闲时补充,避免长时间白屏。

七、交互优化

手段实现效果
数据处理防抖setTimeout(fn, 80) 合并连续调用避免短时间内多次重渲染
数据请求节流_.throttle(fn, 3000)限制接口请求频率
事件按需绑定重新渲染前先 chart.off() 解绑,渲染后重新 on()避免事件监听器堆积
dataZoom 防抖dataZoom 事件触发后防抖再请求细节数据拖拽过程中不频繁请求
legend 交互优化legend 切换时用 merge 而非 notMerge只更新可见性,不重算全部

八、实例生命周期管理

onMounted(() => {
  chart = echarts.init(el)
  // resize 监听
  resizeObserver = new ResizeObserver(() => chart.resize())
  resizeObserver.observe(el)
})

onBeforeUnmount(() => {
  resizeObserver?.disconnect()
  chart?.dispose()    // 必须释放,否则内存泄漏
  chart = null
})

易遗漏点ResizeObserverwindow.resize 监听器必须清理,否则组件卸载后仍触发 chart.resize() 报错。

九、内存与对象管理

问题方案
series 数组反复创建复用对象,用 Object.assign 更新而非每次 {...s}
tooltip formatter 闭包引用旧数据用函数返回最新数据,而非捕获变量
颜色计算结果缓存相同参数的配色结果缓存,切换模式时复用
大数组 GC 压力降采样时预分配 new Array(threshold) 而非 push

十、多图表场景优化

分离模式(多个独立图表)
  │
  ├─ 按可见区域懒渲染:只渲染视口内的图表,滚动时再渲染
  │
  ├─ 共享配置:axis/grid/tooltip 配置抽取为公共对象
  │
  ├─ 并行 batch:多个图表同时处理第一批,而非串行
  │
  └─ 独立策略:每个图表根据自身数据量独立决定降采样力度

十一、优化决策流程图

数据量评估
  │
  ├─ 单条 < 500 点 → 默认配置即可
  │
  ├─ 单条 500~2000 点 → animation:false + sampling:'lttb'
  │
  ├─ 单条 > 2000 点 → 前端 LTTB 降采样到 500 点 + large:true
  │
  ├─ series 数 > 50 → progressive 渐进渲染 + showSymbol:false
  │
  ├─ series 数 > 200 → 激进降采样(48点/条) + appendData 分批
  │
  └─ 总点数 > 100000 → 服务端降采样 + 虚拟滚动 + Web Worker

十二、性能调试要点

  1. 不要用 console.log 打印大数组——会卡死 DevTools
  2. performance.now() 包裹关键阶段——定位是降采样慢还是渲染慢
  3. Chrome Performance 面板看 Long Task——超过 50ms 的任务会阻塞交互
  4. 调试日志必须彻底删除——即使 if(false) 分支也会影响 V8 优化
  5. 对比有/无 symbol 的渲染耗时——通常差 3-5 倍

更多推荐