图表快照必须用fast-copy实现深拷贝,因其能正确处理函数、循环引用、date/map/set等原生类型及原型链,避免json序列化缺陷,并支持定制化跳过不可序列化字段,确保语义正确与业务可用。

在大屏可视化系统中,图表快照常用于状态回滚、审计留痕、多版本对比或异常恢复。但若用 JSON.parse(JSON.stringify()) 或浅拷贝实现,极易因函数丢失、循环引用报错、Date/Map/Set 失效、原型链断裂等问题导致快照不可用。真正可靠的图表快照,必须依赖高性能深拷贝能力——而 fast-copy 正是为此类场景量身优化的方案。
为什么图表快照特别需要 fast-copy?
大屏图表组件(如 ECharts 实例、自定义 Canvas 渲染器、带事件监听器的 React 组件状态)往往包含:
- 嵌套极深的配置对象(含 callback 函数、thunk、Promise 状态)
- 运行时生成的 Map/Set/TypedArray/Blob 等原生结构
- 实例级属性(如
chartInstance._dom、chartInstance.getModel()返回的模型对象) - 隐式循环引用(例如图表配置中引用了自身图例项,或数据项反向持有视图句柄)
fast-copy 的类型分发机制能精准识别并调用对应拷贝器(如对 Date 直接 new Date(value),对 Map 迭代重建),WeakMap 缓存则天然规避循环引用死循环,同时保留构造函数与原型链,确保拷贝后的图表状态对象仍可被原有校验逻辑、diff 算法或序列化工具正确识别。
在图表快照流程中集成 fast-copy
以主流大屏编辑器架构为例,可在“保存快照”动作触发时插入 fast-copy 调用:
- 不直接存储原始 chartOptions 对象,而是执行
const snapshot = fastCopy(chartOptions, { array: 'structuredClone' }) - 若图表状态含不可序列化字段(如 canvas 引用、WebSocket 实例),提前通过
options.custom配置跳过或替换(例如将canvas: null) - 对整个大屏画布状态做快照时,遍历图层区所有组件实例,统一用 fast-copy 批量克隆其
props与state,再组合为完整快照对象 - 搭配 WeakMap 缓存已拷贝过的复杂子对象(如共享的数据集缓存对象),避免重复深拷贝开销
避免常见陷阱:快照 ≠ 全量内存复制
fast-copy 解决的是“语义正确性”,但快照设计还需兼顾业务意图:
- 不要拷贝 DOM 节点、canvas 元素、event handler 引用等运行时资源——它们属于视图层,快照只需记录可重建的配置与数据
- 对只读元数据(如组件 ID、创建时间戳)可考虑浅拷贝以节省性能;对动态数据(如实时更新的 series.data)必须深拷贝
- 若图表使用 WebGL 上下文或大量 GPU 资源,快照中应剔除
glContext、texture等字段,改用 lazy-reinit 机制按需重建 - 测试快照还原能力:用拷贝结果重新初始化图表,验证渲染结果、交互响应、数据联动是否与原状态一致
性能实测建议:快照不是越频繁越好
即使使用 fast-copy,高频快照仍有成本。建议结合业务节奏控制:
- 用户手动点击“保存快照”时必触发一次全量 deepCopy
- 自动快照采用节流策略(如每 30 秒最多 1 次),且仅 deepCopy 变更过的组件配置(配合 useDeepCompareEffect 或 immer 的 produceWithPatches)
- 对超大型图表(series > 10 万点),启用分块快照:先拷贝 schema 和 metadata,数据体用 hash 标识+懒加载,降低单次内存峰值
- 上线前用真实大屏数据跑基准测试:对比 fast-copy 与 JSON 方案在 100ms 内完成快照的成功率与内存增长幅度










