引言
多数前端工程师第一次见到 Vega-Lite 规格时的反应是「这不就是 JSON 版的 ECharts option 吗」。两者的差别远比表面看起来大:ECharts 的 option 是渲染配置,它描述「怎么画」;Vega-Lite 的规格是图表定义,它描述「这张图是什么」——数据从哪来、经过哪些统计变换、哪一列映射到哪个视觉通道。前者是命令式的渲染指令,后者是可被工具读写、可被程序生成、可被静态校验的声明式文档。
这个定位差异带来了三个工程价值。可序列化:规格是纯 JSON,能存进数据库、通过 API 传输、在版本控制里 diff。可复现:同一份规格在任何渲染环境下产出同一张图,因为它不依赖命令式代码的执行顺序。可生成:因为规格有严格的 JSON Schema,LLM 或可视化推荐系统可以直接产出规格,再由 schema 校验器兜底——这正是 Metabase、Superset 这类平台把图表定义存成规格的原因。
难点在于规格的表达能力边界。Vega-Lite 能表达绝大多数标准图表,但一旦需要自定义布局算法、非标准几何对象或与 DOM 的深度交互,规格体系就无能为力,此时必须下沉到 Vega 的 signals 层,或者干脆换回 D3.js 与图形语法 的命令式原语。本文按「定位 → 规格结构 → 编码 → 转换 → 复合 → 交互 → 渲染 → 生成 → 性能」的顺序展开,全部示例基于 Vega-Lite 5.16 与 Vega 5.24。
目录
- Vega 与 Vega-Lite 的关系与定位
- 规格的三层结构:数据、标记、编码
- 编码通道与类型系统
- transform 数据转换体系
- 复合视图:分层、拼接、重复与分面
- 交互与 params 参数系统
- 比例尺、坐标轴与图例配置
- 表达式与条件编码
- 渲染后端与 embed API
- 规格生成与程序化构建
- 性能边界与大规模数据
- 与 G2、D3 的分工与组合
1. Vega 与 Vega-Lite 的关系与定位
Vega 是底层可视化语法,它把一张图拆成数据(data)、比例尺(scales)、标记(marks)、以及信号(signals)四类声明,其中 marks 是带 enter/update/exit 属性的嵌套集合,signals 是响应式的表达式变量,用来驱动交互。它接近「JSON 化的 D3」——表达力强,但写一个带 tooltip 的柱状图要几百行。
Vega-Lite 是高阶封装,它把常见的图表模式抽象成「数据 + 标记 + 编码」,然后编译成 Vega 规格。编译过程不是简单映射,而是包含大量自动推断:Vega-Lite 会替你决定用什么比例尺、轴怎么排、图例放哪、tooltip 显示什么字段。这个编译链是 Vega-Lite 的核心资产,也是它与 ECharts option 的本质区别——option 是终点,Vega-Lite 规格是起点。
Vega-Lite 规格 (JSON)
│ 编译 vega-lite 编译器
▼
Vega 规格 (JSON) ←── 也可手写,表达力更强
│ vega runtime
▼
Canvas / SVG 渲染 + 交互信号
选型上的判断是:能用 Vega-Lite 表达的,不要手写 Vega;需要自定义标记、自定义布局或复杂信号联动的,才下沉到 Vega。两者共用同一套运行时,所以可以混用——在 Vega 里嵌入一段 Vega-Lite 编译结果。与 G2 的对比见 AntV G2 声明式图表体系 ,G2 同样基于图形语法,但把交互语法也做成了声明式,而 Vega-Lite 的交互走 params。
2. 规格的三层结构:数据、标记、编码
一份最小规格由三个顶层键组成,这也是理解整个体系的入口:
{
"$schema": "https://vega.github.io/schema/vega-lite/v5.json",
"data": { "url": "sales.csv" },
"mark": "bar",
"encoding": {
"x": { "field": "region", "type": "nominal", "sort": "-y" },
"y": { "field": "sales", "type": "quantitative", "aggregate": "sum" }
}
}
data 有三种形态:values 内联数组(适合小数据与示例)、url 指向 CSV/JSON(运行时拉取,支持 format 指定解析)、name 引用已注册的数据集(在 embed 时通过 view.data() 注入,适合大数组避免序列化进规格)。还有一种 sequence 生成等差数列,用于函数曲线与背景网格。
mark 可以是字符串("bar"、"line"、"point"、"area"、"rect"、"arc"、"rule"、"tick"、"boxplot")或对象。对象形态下可配置 point、line、tooltip、interpolate、opacity、strokeWidth 等。复合标记如 "boxplot"、"errorband" 是语法糖,编译时会展开成多层。
encoding 是核心:它把字段映射到视觉通道,并声明每个字段的类型。字段还可以内联 aggregate(聚合)、bin(分箱)、timeUnit(时间粒度),这些都会在编译时转成 transform。Vega-Lite 的编码层是「隐式转换层」——你写 "aggregate": "sum",编译器自动插入一个 aggregate transform;写 "bin": true,自动插入 bin transform 并生成两个派生字段。
除这三个键,还有一组顶层布局属性常被忽略:width/height 指定单视图尺寸(数字为像素,"container" 表示自适应父容器),autosize 控制尺寸计算策略(pad、fit、none、fit-x、fit-y),config 提供全局默认样式,background 设画布底色,padding 设内边距。
{
"width": "container",
"height": 320,
"autosize": { "type": "fit", "contains": "padding" },
"padding": 5,
"background": "transparent"
}
width: "container" 配合 autosize: "fit" 是做响应式图表的标准组合:容器宽度变化时视图自动重排。不要用 CSS 强行拉伸 canvas——那只会把图形拉变形,Vega-Lite 内部的比例尺不会重算。响应式必须走 autosize,让编译器重新计算布局。
3. 编码通道与类型系统
通道决定了「这个字段用什么视觉属性表达」。Vega-Lite 支持的通道分几类:
| 类别 | 通道 | 说明 |
|---|---|---|
| 位置 | x y x2 y2 theta radius | 直角/极坐标系的位置 |
| 颜色 | color fill stroke strokeDash | 分类或连续色 |
| 大小 | size strokeWidth | 点面积、线宽 |
| 形状 | shape angle | 点的符号、角度 |
| 文本 | text tooltip href | 标签、悬浮内容、超链接 |
| 顺序 | order detail row column facet | 排序、分组、分面 |
类型系统只有五个值:quantitative(Q,连续数值)、temporal(T,时间)、ordinal(O,有序离散)、nominal(N,无序离散)、geojson(G,地理)。类型决定了默认比例尺与默认轴的形态——Q 用线性比例尺加连续轴,N 用 band 比例尺加离散轴,T 用时间比例尺。类型写错是最高频的错误:把年份写成 quantitative 会得到一条把 2020、2021 当数值等距排布的轴,而正确的 temporal 或 ordinal 才符合阅读预期。
{
"encoding": {
"x": { "field": "month", "type": "ordinal", "timeUnit": "month" },
"y": { "field": "revenue", "type": "quantitative", "scale": { "zero": false } },
"color": { "field": "channel", "type": "nominal" },
"size": { "field": "orders", "type": "quantitative" },
"tooltip": [
{ "field": "revenue", "type": "quantitative", "format": ",.0f" },
{ "field": "channel", "type": "nominal" }
]
}
}
tooltip 是唯一接受数组的通道,用来声明悬浮时显示哪些字段。format 用 d3-format 的格式串(,.0f 表示千分位无小数)。
4. transform 数据转换体系
transform 是 Vega-Lite 区别于「纯配置」的关键。它是一组有序的转换步骤,在渲染前对数据流水线式处理:
{
"data": { "url": "orders.csv" },
"transform": [
{ "filter": "datum.status !== 'cancelled'" },
{ "calculate": "datum.price * datum.qty", "as": "amount" },
{ "timeUnit": "yearmonth", "field": "order_date", "as": "month" },
{ "aggregate": [{ "op": "sum", "field": "amount", "as": "total" }],
"groupby": ["month", "region"] },
{ "window": [{ "op": "rank", "as": "rk" }], "sort": [{ "field": "total", "order": "desc" }] },
{ "filter": "datum.rk <= 10" }
],
"mark": "line",
"encoding": { "x": { "field": "month", "type": "temporal" },
"y": { "field": "total", "type": "quantitative" },
"color": { "field": "region", "type": "nominal" } }
}
常用 transform 可归为几类。行级:filter(按表达式筛行)、calculate(用表达式算新列)、impute(补缺失值)。聚合:aggregate(分组聚合)、joinaggregate(把聚合结果并回原行,用于算占比)、window(排名、累计、滑动窗口)、stack(堆叠偏移)。结构:fold(宽表转长表)、pivot(长表转宽表)、flatten、lookup(跨数据集关联)。统计:bin、timeUnit、density、regression、loess、quantile、sample。
转换的位置是有边界的。Vega-Lite 的 transform 在浏览器端执行,数据量超过约十万行时编译与计算会明显变慢。真正的大规模聚合、跨表关联应该放在 OLAP 侧预计算,前端只做展示层调整——这与 可视化与 OLAP 数仓集成
的分工一致。sample transform 可以在不改变图形形态的前提下随机抽样,是前端兜底的手段。
5. 复合视图:分层、拼接、重复与分面
单视图只能画一张图。复合视图把多张图组合起来,是 Vega-Lite 相对 ECharts option 最优雅的部分——组合是规格层面的组合,不需要手写坐标偏移。
{
"data": { "url": "stock.csv" },
"layer": [
{ "mark": "line", "encoding": { "x": {"field":"date","type":"temporal"},
"y": { "field": "price", "type": "quantitative" } } },
{ "mark": { "type": "rule", "color": "red" },
"encoding": { "y": { "datum": 100 } } }
]
}
四类复合操作:layer 把多个视图叠在同一坐标系(线 + 阈值线 + 面积);hconcat / vconcat / concat 水平或垂直拼接多个独立视图;repeat 对同一份编码在不同字段上重复(如对多个指标画相同的小倍数图);facet 按字段值切分数据并生成子图网格。facet 与 row/column 通道的区别是:通道形态更简洁,但 facet 操作符支持更细的 resolve 控制。
resolve 决定子图之间是否共享比例尺:
{
"facet": { "field": "region", "type": "nominal", "columns": 3 },
"spec": { "mark": "line", "encoding": { "x": {...}, "y": {...} } },
"resolve": { "scale": { "y": "independent" }, "axis": { "x": "shared" } }
}
shared(默认)让所有子图共用同一比例尺,便于横向比较量级;independent 让每个子图自适应用自己的范围,便于看清各自形状但会制造虚假的量级对比——小倍数图的可比性是它最大的价值,除非明确知道每个子图范围差异极大,否则应该用 shared。
repeat 是生成小倍数图最省事的写法。它把同一份 spec 在不同字段或不同取值上重复,避免手写多份几乎相同的规格:
{
"data": { "url": "metrics.csv" },
"repeat": { "column": ["gmv", "orders", "aov", "refund_rate"] },
"spec": {
"mark": { "type": "line", "point": true },
"encoding": {
"x": { "field": "date", "type": "temporal" },
"y": { "field": { "repeat": "column" }, "type": "quantitative" }
}
}
}
这里 y 的 field 用了 { "repeat": "column" },编译器会把它替换成当前列对应的字段名。repeat 与 facet 的区别是:facet 按数据字段的取值切分(同一个字段的不同值),repeat 按规格里列出的字段名重复(不同字段的同一份编码)。前者回答「每个区域各自什么样」,后者回答「每个指标各自什么样」。
6. 交互与 params 参数系统
Vega-Lite 5 把旧版的 selection 重构成了 params,交互变成了「声明一个参数 + 用参数影响编码」。这是它最有辨识度的能力。
{
"params": [
{ "name": "brush", "select": { "type": "interval", "encodings": ["x"] },
"bind": "scales" },
{ "name": "highlight", "select": { "type": "point", "on": "mouseover",
"encodings": ["color"] } },
{ "name": "threshold", "value": 100,
"bind": { "input": "range", "min": 0, "max": 500, "step": 10 } }
],
"mark": "point",
"encoding": {
"x": { "field": "x", "type": "quantitative" },
"y": { "field": "y", "type": "quantitative" },
"opacity": {
"condition": { "param": "highlight", "value": 1 },
"value": 0.2
}
}
}
三种参数形态:select 参数由用户手势驱动(interval 框选、point 点选),bind: "scales" 让框选直接变成缩放平移;value 参数是可被信号或外部 API 修改的变量;bind 输入把参数绑定到 HTML 控件(range、select、checkbox),Vega-Lite 自动生成控件。参数值可以在运行时通过 view.signal('threshold', 200) 从外部改写,这让规格能与宿主应用的 UI 双向联动。
联动(cross-filtering)在 Vega-Lite 里是「一个视图的 select 参数被另一个视图的 condition 引用」。因为参数是规格级别的共享变量,跨视图联动不需要事件总线——只要两个视图在同一份规格内、引用同名参数即可。这是声明式相对命令式联动最省事的地方。
7. 比例尺、坐标轴与图例配置
比例尺(scale)把数据域映射到视觉范围。Vega-Lite 会根据字段类型自动选默认比例尺,但几乎所有细节都可覆盖:
{
"encoding": {
"y": {
"field": "price", "type": "quantitative",
"scale": { "type": "log", "domain": [1, 10000], "nice": true, "zero": false, "clamp": true },
"axis": { "title": "价格(元)", "format": "~s", "grid": true, "labelAngle": 0 }
},
"color": {
"field": "category", "type": "nominal",
"scale": { "scheme": "tableau10" },
"legend": { "orient": "right", "columns": 1, "symbolType": "circle" }
}
}
}
比例尺类型覆盖 linear、log、pow、sqrt、symlog、time、utc、band、point、ordinal、quantile、quantize、threshold。连续色用 scheme 指定调色板名(viridis、blues、redblue 等 d3-scale-chromatic 内置方案),离散色用 scheme: "tableau10" 或自定义 range 数组。
轴的三个高频配置:format 用 d3-format(~s 是 SI 前缀,% 是百分比,.2f 是两位小数),labelAngle 控制标签旋转(类别名长时设 -45 或 0),grid 开关网格线。zero: false 让轴不从零起——这在折线图上常被误用:截断轴会放大幅度差异,只在明确知道量级差异小、且图上标注了截断时才用,否则就是误导性图表。
8. 表达式与条件编码
表达式(expression)是 Vega-Lite 的逃生舱:当内置配置无法表达某个逻辑时,用 Vega 表达式语言写一段小程序。表达式可访问 datum(当前数据项)、datum 的字段、以及 param 参数值。
{
"transform": [
{ "calculate": "datum.profit / datum.revenue", "as": "margin" },
{ "calculate": "datum.margin > 0.2 ? '高毛利' : '常规'", "as": "tier" }
],
"encoding": {
"color": {
"condition": { "test": "datum.margin < 0", "value": "#d64545" },
"value": "#2f6fed"
}
}
}
条件编码(condition)有两条路径:test 走表达式,param 走交互参数。两者可以嵌套——先判断参数选中,再判断数据条件。条件编码最常见的用途是「选中高亮」:选中项用饱和色,其余用灰色。这与 G2 的 state 概念相通,但 Vega-Lite 把它统一到了编码层,不需要为每种图表类型单独定义状态样式。
表达式语言的能力边界要注意:它没有循环、没有自定义函数、不能访问 DOM。复杂逻辑应放在 calculate 里拆成多步,或者干脆在数据源侧算好。表达式里做重计算是性能陷阱——它会对每一行求值,几十万行时 datum.a * datum.b / datum.c 会拖慢编译。
9. 渲染后端与 embed API
vega-embed 是把规格挂到页面上的标准入口,版本 6.x:
import embed from 'vega-embed';
const result = await embed('#view', spec, {
renderer: 'canvas', // 或 'svg'
actions: { export: true, source: false, compiled: false, editor: true },
theme: 'dark',
config: { background: 'transparent', font: 'Inter, "PingFang SC", sans-serif' },
});
// result.view 是 Vega View 实例,可编程控制
await result.view.signal('threshold', 200).runAsync();
result.view.addEventListener('click', (event, item) => { /* 数据项点击 */ });
renderer 的选择与 ECharts 同理:元素多选 Canvas,需要 DOM 可访问性或矢量导出选 SVG。actions 控制右上角的菜单(导出 PNG/SVG、查看源码、查看编译后的 Vega 规格、打开在线编辑器)。config 是全局配置,可用来统一字体、背景、色板——用 config 而不是逐图改样式,这与 ECharts 主题的思路一致。
View 实例提供了规格之外的编程接口:view.signal() 读写参数、view.data() 替换数据集、view.runAsync() 触发重渲染、view.toImageURL() 导出图片。把 view 当作受控组件的 ref:外部状态变化时改 signal 或 data,而不是重新 embed 一份新规格——后者会重建整个运行时,代价高昂。
主题(theme)是跨图表统一样式的推荐手段。Vega-Lite 内置 default、dark、excel、fivethirtyeight、ggplot2、quartz 等主题,也可以传入自定义 config 对象。用 config 而不是逐图改样式,与 ECharts 主题、G2 主题的思路一致:
embed('#view', spec, {
config: {
background: 'transparent',
axis: { labelFont: 'Inter', titleFontSize: 12, gridColor: '#f0f0f0' },
legend: { orient: 'bottom', direction: 'horizontal' },
range: { category: ['#2f6fed', '#22a06b', '#e8a33d', '#d64545', '#7a5af8'] },
view: { stroke: null },
},
});
range.category 覆盖离散色板,axis/legend/view 覆盖各组件的默认样式。规格里的显式配置优先级高于 config,所以 config 只应放「全局默认」,个别图表需要特殊处理时在规格里覆盖即可。
调试规格有两个入口:actions.compiled 打开后可以看到编译生成的 Vega 规格,理解「我写的编码最终变成了什么」;在线编辑器(actions.editor)支持实时改规格看效果。看懂编译产物是排查 Vega-Lite 疑难问题的关键——很多「为什么轴是这样」「为什么多了一层 group」的疑问,答案都在编译后的 Vega 里。
10. 规格生成与程序化构建
Vega-Lite 的最大工程价值是规格可被程序生成。三条路径。
其一,TypeScript 类型约束。vega-lite 包导出 TopLevelSpec 等类型,可以在代码里构造规格并享受类型检查。这比手写 JSON 更安全,也能被 IDE 补全。
import type { TopLevelSpec } from 'vega-lite';
export function buildBarSpec(data: unknown[], field: string): TopLevelSpec {
return {
$schema: 'https://vega.github.io/schema/vega-lite/v5.json',
data: { values: data },
mark: { type: 'bar', tooltip: true },
encoding: {
x: { field, type: 'nominal', sort: '-y' },
y: { aggregate: 'count', type: 'quantitative' },
},
};
}
其二,JSON Schema 校验。Vega-Lite 提供完整的 JSON Schema,任何生成器(包括 LLM)产出的规格都能先校验再渲染,避免非法规格导致的白屏。其三,跨语言绑定。Python 的 Altair、R 的 vegawidget 都是同一套规格的封装,后端算完数据直接产出规格,前端只负责 embed——数据与可视化定义分离,是前后端协作的干净边界。
把规格存进数据库是 BI 平台的标准做法:一张表存 spec JSON,一张表存 data source 引用,图表就是「规格 + 数据源」的组合。这与 Metabase 轻量 BI 平台
的模型一致。
11. 性能边界与大规模数据
Vega-Lite 的性能瓶颈在编译与 transform 在浏览器端执行。经验边界:
| 数据量 | 表现 | 建议 |
|---|---|---|
| < 1 万行 | 流畅 | 直接用 transform |
| 1 万 ~ 10 万行 | 首屏延迟明显 | 预聚合后再传入 |
| > 10 万行 | 编译与计算卡顿 | 服务端聚合 + 只传聚合结果 |
四条优化手段。预转换:把 aggregate、bin 这类重计算挪到后端,前端规格只做展示。只传必要列:values 里带上用不到的字段会成倍放大序列化开销。用 sample 抽样:对散点图这类「点太多反而看不清」的图形,抽样到几千点几乎不损失信息。切 Canvas 渲染器:SVG 在元素上千后会因 DOM 开销变慢,切换渲染后端能显著提速。
还有一个隐蔽的坑:规格里的内联数据会随规格一起被序列化进网络请求。十万行的 values 会让规格 JSON 膨胀到几十 MB。正确做法是用 name 引用数据集,在 embed 时通过 view.data('source', rows) 注入,或让 url 指向一个已聚合的接口。这与实时流式图表里「只传增量」的思路互补。
流式更新时,Vega 的 dataflow 会做增量传播:view.data('source', newRows) 之后只重算依赖该数据集的 transform 与 mark,不会整体重建。但要注意两点:一是每次 change 都会重新跑一遍前端 transform,所以实时场景下前端 transform 应尽量轻;二是 view.runAsync() 是异步的,高频调用时要合并多次更新,避免渲染队列积压。对每秒多次更新的场景,更好的做法是在数据侧做滑动窗口,只把窗口内的数据推给 view。
12. 与 G2、D3 的分工与组合
三者的定位可以放在一条「表达力—易用性」的轴上:
| 维度 | Vega-Lite | G2 | D3 |
|---|---|---|---|
| 范式 | 声明式规格 | 图形语法 + 交互语法 | 命令式原语 |
| 规格可序列化 | 是(纯 JSON) | 部分(JS 配置) | 否 |
| 交互定义 | params 声明 | 交互语法声明 | 手写事件 |
| 自定义图形 | 需下沉 Vega | 自定义 shape | 任意 |
| 适合 | 标准图表、规格化、程序生成 | 交互丰富的业务图表 | 发明新图形 |
判断顺序:先问「这张图能否用标准图形语法表达」——能,用 Vega-Lite(规格可存可生成);需要丰富交互且已在 G2 生态里,用 G2;需要超出图形语法能力(自定义布局、非标准几何、与 DOM 深度交互),才用 D3。三者可以组合:Vega-Lite 处理 90% 的标准图表,D3 写剩下的定制组件,G2 负责交互密集的业务大屏。
Vega-Lite 编译到 Vega 这一层是它的独特优势——规格既是声明式的输入,也是可读的中间产物。当规格无法表达时,可以直接手写 Vega(增加 signals、自定义 mark),而不必整体换栈。这条渐进式下沉的路径,是它相比 ECharts option 与 G2 配置更适合做「平台化图表定义」的根本原因。
权衡取舍
| 决策点 | 选项 A | 选项 B | 何时选 A | 何时选 B |
|---|---|---|---|---|
| 抽象层 | Vega-Lite | Vega | 标准图表 | 需自定义信号/标记 |
| 数据位置 | 内联 values | 命名数据集注入 | 数据 <1 万行 | 大数组、避免序列化 |
| 转换位置 | 前端 transform | 服务端预聚合 | 行数 <10 万 | 行数 >10 万 |
| 渲染器 | Canvas | SVG | 元素多、交互频繁 | 需无障碍/矢量导出 |
| 分面比例尺 | shared | independent | 需横向比较量级 | 各子图范围差异极大 |
| 交互 | params 声明 | 宿主应用控制 | 规格内联动 | 与外部 UI 深度耦合 |
| 生成方式 | 手写 JSON | TS 类型/后端生成 | 少量固定图表 | 平台化、LLM 生成 |
常见坑清单
- 字段类型写错——把年份当 quantitative 得到等距数值轴;按语义选 Q/T/O/N,时间用 temporal。
- 内联大数组进规格——规格 JSON 膨胀到几十 MB 拖慢网络;改用
name引用并注入。 - 前端做重聚合——十万行以上 transform 卡顿;把聚合下沉到 OLAP 侧。
- 分面用 independent 比例尺——各子图量级不可比,制造虚假对比;默认用 shared。
- 表达式里做重计算——逐行求值的复杂表达式拖慢编译;拆成多步 calculate 或预计算。
- 重新 embed 更新数据——重建整个运行时,代价高;改用 view.data()/signal() 增量更新。
- SVG 渲染上万元素——DOM 开销导致交互卡顿;元素多时切 Canvas。
- 截断轴不标注——
zero: false放大差异却无提示;截断必须在图上明确标注。 - select 参数名冲突——多个视图引用同名参数导致意外联动;参数按作用域命名。
- 忽略 JSON Schema 校验——非法规格直接白屏;生成后先校验再 embed。
小结
Vega-Lite 的核心价值不在「换一种方式画图」,而在把图表变成可序列化、可复现、可被程序生成的文档。数据、标记、编码三层结构把「这张图是什么」讲清楚,transform 体系让统计变换也进入规格,复合视图与 params 把组合与交互提升到声明层。这些能力共同支撑了 BI 平台、LLM 生成图表、跨语言可视化绑定等场景。
工程上的取舍很清晰:标准图表用声明式规格,重计算下沉到数据侧,大数组用命名数据集注入,元素多时切 Canvas。当规格无法表达需求时,沿「Vega-Lite → Vega → D3」的阶梯逐级下沉,而不是整体换栈。
下一步可以对照 AntV G2 的声明式实现,看它如何把交互也纳入语法;或回到 D3 的图形语法,理解规格体系底层的原语;流式数据下的增量渲染则是一套独立的工程方法。
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。