搜索文章

输入关键词开始搜索

ECharts扩展新的生命周期

ECharts#ECharts#数据可视化

这篇不是在整理 ECharts 的通用扩展教程,而是记录一次具体需求的实现过程:在不直接修改业务代码的情况下,为 ECharts 扩展出新的生命周期,并在这个过程中处理组件类型识别、坐标系渲染时机和 patch-package 的问题。

需求

项目中需要在 ECharts 更新数据时,插入一个自定义的回调时机(ECharts 原生没有暴露这个点)。我们的做法是通过 patch-package 直接改 ECharts 的源码来扩展一个新的生命周期:beforeupdate。

1

ECharts 的生命周期设计

1

技术方案

生命周期的时机

interface LifecycleEvents {
  afterinit: [EChartsType];
  // mergeOption
  // restoreData
  // 应该加在这里 component:beforeupdate
  'series:beforeupdate': [GlobalModel, ExtensionAPI, UpdateLifecycleParams];
  //'render,
  // grid calculateGridLayout
  // coordinatesystem:1. update 2.render 3.animation
  'series:layoutlabels': [GlobalModel, ExtensionAPI, UpdateLifecycleParams];
  'series:transition': [GlobalModel, ExtensionAPI, UpdateLifecycleParams];
  'series:afterupdate': [GlobalModel, ExtensionAPI, UpdateLifecycleParams];
  // 'series:beforeeachupdate': [GlobalModel, ExtensionAPI, SeriesModel]
  // 'series:aftereachupdate': [GlobalModel, ExtensionAPI, SeriesModel]
  afterupdate: [GlobalModel, ExtensionAPI];
}

应该加在renderComponents()里面,类似renderSeries()

1

无法识别组件类型

同事反馈 anid 不可用,因此不能扩展成component:beforeupdate,只能扩展成beforeupdate

得找到坐标系渲染完成的时机:

AxisView.ts

render(axisModel: AxisBaseModel, ecModel: GlobalModel, api: ExtensionAPI, payload: Payload) {
  // FIXME
  // This process should proformed after coordinate systems updated
  // (axis scale updated), and should be performed each time update.
  // So put it here temporarily, although it is not appropriate to
  // put a model-writing procedure in `view`.
}

patch-package

通过 patch-package 修改第三方 npm 包的内容

github: https://github.com/ds300/patch-package

别人的使用笔记: https://juejin.cn/post/6962554654643191815

https://www.jianshu.com/p/f83e7cc93bf5

其他的方案: https://juejin.cn/post/6962554654643191815

不知道是否支持修改 TypeScript 的源码包。

patch 文件太大的问题

最近有功能需要给 ECharts 扩展一个生命周期,修改 ECharts 源码后,打包出来的 patch 文件非常大,上万行。

经排查,发现是因为package.json中的这个脚本大量改动了 echarts 文件导致的:

“postprepare”: “node scripts/enableEChartsLibsDTS.mjs”, 因此后续需要通过 patch-package 修改 ECharts 源码时,请按照如下流程操作:

1、注释掉 scripts/enableEChartsLibsDTS.mjs 的这一行:

await copyFiles(SOURCE_DTS_DIRECTORY, TARGET_DTS_DIRECTORY);

2、删除 node_modules/echarts 这个包

3、npm i 安装 echarts

4、修改 echarts 源码

5、执行如下命令,生成 patch 文件:

npx patch-package echarts

6、恢复第一步的注释

7、提交代码

尺寸单位转换为数值单位

以柱状图的柱子宽度barWidth为例,它的类型是number | string

其转换逻辑位于barGrid.ts中的makeColumnLayout()

// seriesModel.get('barWidth')是用户配置的柱子宽度,比如'20%'、'20px'、20
const barWidth = parsePercent(seriesModel.get('barWidth'), bandWidth);

parsePercent这个函数仅支持 px 和%,并不支持 rem、em 等单位。

src/util/number.ts

/**
 * Convert a percent string to absolute number.
 * Returns NaN if percent is not a valid string or number
 */
export function parsePercent(percent: number | string, all: number): number {
  switch (percent) {
    case 'center':
    case 'middle':
      percent = '50%';
      break;
    case 'left':
    case 'top':
      percent = '0%';
      break;
    case 'right':
    case 'bottom':
      percent = '100%';
      break;
  }
  if (zrUtil.isString(percent)) {
    if (_trim(percent).match(/%$/)) {
      return (parseFloat(percent) / 100) * all;
    }

    return parseFloat(percent);
  }

  return percent == null ? NaN : +percent;
}

经验教训

  • 先确认目标代码真正所在的生命周期位置,再决定监听哪个事件。组件类型识别不出来时,不能想当然地把所有组件都当成同一种处理。
  • 使用 patch-package 前,要先确认构建流程会不会重新生成或覆盖被 patch 的文件,否则 patch 文件会变得很大,后续也难以维护。
  • 生命周期扩展的关键不只是“能不能触发”,还要确认触发时坐标系或组件已经完成了需要依赖的渲染步骤。