ECharts扩展新的生命周期
这篇不是在整理 ECharts 的通用扩展教程,而是记录一次具体需求的实现过程:在不直接修改业务代码的情况下,为 ECharts 扩展出新的生命周期,并在这个过程中处理组件类型识别、坐标系渲染时机和 patch-package 的问题。
需求
项目中需要在 ECharts 更新数据时,插入一个自定义的回调时机(ECharts 原生没有暴露这个点)。我们的做法是通过 patch-package 直接改 ECharts 的源码来扩展一个新的生命周期:beforeupdate。

ECharts 的生命周期设计

技术方案
生命周期的时机
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():

无法识别组件类型
同事反馈 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 文件会变得很大,后续也难以维护。
- 生命周期扩展的关键不只是“能不能触发”,还要确认触发时坐标系或组件已经完成了需要依赖的渲染步骤。