Appearance
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 索引错位会导致数据污染。将两类路径拆开处理,可以分别保持编辑与刷新阶段的稳定行为。
