基于前文,我们已介绍了 VChart 图表组成和 Tree Shaking 概念。基于这两个前置概念,接下来将详细阐述 VChart 按需加载的具体实现。
VChart图表的核心创建流程

Factory
VChart 的按需加载主要依托 Factory 类来达成。该类承担着重要职责,负责对各类图表、系列、组件、图元、Region、布局以及插件进行注册和创建。下面将从注册机制、创建机制以及图表的按需注册这几个方面展开深入分析。
注册机制
Factory 类提供了一系列静态方法,用于注册不同类型的模块。通过这些方法,模块被注册到 Factory 的静态属性中,进而形成一个注册表。具体代码如下:
static registerChart(key: string, chart: IChartConstructor) { Factory._charts[key] = chart; } static registerSeries(key: string, series: ISeriesConstructor) { Factory._series[key] = series; } static registerComponent(key: string, cmp: IComponentConstructor, alwaysCheck?: boolean) { Factory._components[key] = { cmp, alwaysCheck }; } // 其他注册方法...
创建机制
Factory 类同样提供了一系列静态方法,用于按需创建模块实例。这些方法会依据类型从注册表中查找对应的构造函数,并创建实例。示例代码如下:
static createChart(chartType: string, spec: any, options: IChartOption): IChart | null { if (!Factory._charts[chartType]) { return null; } const ChartConstructor = Factory._charts[chartType]; return new ChartConstructor(spec, options); } static createSeries(seriesType: string, spec: any, options: ISeriesOption) { if (!Factory._series[seriesType]) { return null; } const SeriesConstructor = Factory._series[seriesType]; return new SeriesConstructor(spec, options); } // 其他创建方法...
图表的按需注册
以线图为例,下面来看具体的按需注册实现。在 packages/vchart/src/chart/line/line.ts 中,有如下代码:
export const registerLineChart = () => {
registerLineSeries();
Factory.registerChart(LineChart.type, LineChart);
};
其中 registerLineSeries 在 packages/vchart/src/series/line/line.ts 中实现,代码如下:
export const registerLineSeries = () => {
registerSampleTransform();
registerMarkOverlapTransform();
registerLineMark();
registerSymbolMark();
registerLineAnimation();
registerScaleInOutAnimation();
registerCartesianBandAxis();
registerCartesianLinearAxis();
Factory.registerSeries(LineSeries.type, LineSeries);
};
通过上述代码实现,在使用 VChart 时,若调用 registerLineChart 注册线图,就会将线图依赖的线的系列、线图元、点图元等必要元素默认注册。具体使用方式如下:
import { VChart } from './core'; import { registerLineChart } from './chart/line'; VChart.useRegisters([ registerLineChart, // 其他需要的模块 ]);
而在 packages/vchart/src/core/vchart.ts 中,创建图表实例时并非直接通过路径引用,而是借助 Factory.createChart 来创建。如此一来,核心类 vchart 就能在不引用所有图表实现的情况下,依据用户注册的图表创建实例。相关代码如下:
private _initChart(spec: any) { // ... const chart = Factory.createChart(spec.type, spec, this._getChartOption(spec.type)); //... }
核心类 VChart 的按需加载
接下来探讨一个问题:以下两种引用 VChart 的方式是否存在差异?
- 方法一:
import VChart from '@visactor/vchart';
- 方法二:
import { VChart } from '@visactor/vchart';
答案是肯定的,这两种引用方式所产生的效果不同。下面分析其差异原因。
首先,在 vchart 代码库对应的 package.json 中,可看到如下配置:
{ "sideEffects": [ "./*/index-lark.js", "./*/index-wx-simple.js", "./*/index-wx.js", "./*/vchart-all.js", "./*/vchart-simple.js" ], }
该配置明确声明了所有有副作用的文件。
在 packages/vchart/index.ts 中,有以下导出内容:
import { VChart } from './vchart-all'; export default VChart; export * from './core';
其中 core/index.ts 文件中又有如下导出:
import { VChart } from './vchart'; export { VChart, Factory };
所以方法一等价于如下引用,引用的 VChart 是 vchart - all 导出的 VChart 类,方法二引用的相当于从 core/vchart.ts 导出的 VChart 类。
import { default as VChart } from '@visactor/vchart';
其中文件 vchart-all.ts 的主要作用是注册和导出 VChart 的所有功能模块:
VChart.useRegisters([ // charts registerLineChart, registerAreaChart, // ...其他图表 // components registerCartesianLinearAxis, registerCartesianBandAxis, // ...其他组件 // layout registerGridLayout, registerLayout3d, // mark registerAllMarks, // plugin registerDomTooltipHandler, registerCanvasTooltipHandler, // ...其他插件 ]); export { VChart };
由于 vchart - all 是声明了有副作用的文件,所以当采用方案一引用 VChart 时,会将所有 vchart-all 中依赖的文件都进行打包;而使用方案二引用 VChart 时,仅会打包 core/vchart.ts。