Skip to content

ECharts setOption 的合并策略

ECharts 的 setOption 默认不是替换,而是深度合并。这个行为在不了解时会导致一个很头疼的 bug:你想"删掉"某个配置,只传了空值——结果旧配置还在。


默认 merge 的执行逻辑

ECharts 5 中 setOption 的执行路径:

text
setOption(option, opts)
  → OptionManager.setOption(option, opts)
    → notMerge === false(默认)
      → 深度合并传入 option 与内部 _option
      → 递归处理每个顶层 key(title、tooltip、legend、series...)
      → series 数组按索引合并
    → notMerge === true
      → 直接替换内部 _option

默认 merge 对配置编辑场景是方便的——修改 title.text 只需传入 { title: { text: 'x' } },不需要传完整的 option。

但在数据切换场景下会出问题:

js
// 当前内部状态:option.series = [lineSeries, barSeries](2 个元素)

// 切换为饼图,仅传入 1 个 series
chart.setOption({ series: [pieSeries] });
// → 合并结果:option.series = [merge(pieSeries, lineSeries), barSeries]
// 第一个元素:pieSeries 与 lineSeries 的合并 → 字段污染
// 第二个 barSeries 保留 → 未被移除!

新 series 数组长度小于旧数组时,多余元素不会自动移除。图表类型切换时产生残余配置。


三种控制模式

notMerge: true —— 完全替换

js
chart.setOption(fullOption, { notMerge: true });

传入的 option 完全替换内部状态。适合数据源切换、图表类型切换——需要保证 option 完整且独立的场景。

replaceMerge —— 选择性替换

js
chart.setOption(newOption, { replaceMerge: ['series'] });

指定某些顶层 key 走 replace 模式,其余走 merge。适合"我只想替换 series 但保留 tooltip/legend 等配置"的场景。

lazyUpdate —— 延迟生效

js
chart.setOption(option, { lazyUpdate: true });

Model 更新延迟到下一个 requestAnimationFrame 执行。用于动画进行中调用 setOption 时避免中断动画。默认 lazyUpdate: false(同步更新 Model)。


策略分离:配置编辑 vs 数据刷新

在生产代码中,区分两类 setOption 调用场景可以避免大部分合并问题:

text
配置变更路径:setOption(partial)           // notMerge: false(默认 merge)
数据变更路径:setOption(fullOption, { notMerge: true })  // replace

配置变更(面板交互):

  • 高频(拖动滑块可达 60fps 触发频率)
  • 每次变更字段少(1-3 个 key)
  • merge 避免全量 option 构建开销

数据变更:

  • 低频(秒级)
  • 需要保证 series 数组完全替换
  • 全量构建 + replace 的 CPU 开销可接受

如果全部使用 replace:高频配置交互中每次构建完整 option 的 CPU 开销(通用配置 + series encode 全部重建)在单图场景通常仍在 16ms 帧预算内,但在 10+ 图表实例同时更新时会累积。如果全部使用 merge:series 索引错位会导致数据污染。将两类路径拆开处理,可以分别保持编辑与刷新阶段的稳定行为。