@astryxdesign/vega:在 React 中渲染 Vega / Vega-Lite 规范的 Astryx 图表封装组件指南 📅 发布时间:2026/9/15 17:15:52 👁 浏览次数: astryxdesign/vega在 React 中渲染 Vega / Vega-Lite 规范的 Astryx 图表封装组件指南【免费下载链接】astryxAn open source design system thats fully customizable and agent ready项目地址: https://gitcode.com/GitHub_Trending/as/astryxastryxdesign/vega是 Astryx 设计系统提供的 Vega 图表封装包它以单个 React 组件VegaChart为入口将 Vega 与 Vega-Lite 的完整能力编译、解析、View 生命周期透传给 React 应用。本文从安装、用法、完整 API 到 View 生命周期按值比较的重建策略、数据加载契约与不受信任 spec 的安全边界结合包内源码VegaChart.tsx、viewInputs.ts、schema.ts 等与测试用例逐层展开。读完本文你将能够正确安装并组合 Vega/Vega-Lite 渲染管线、精通parseConfig/parseOptions/viewOptions/compileOptions四个透传配置的取值与作用、理解组件按值而非按引用重建 View 的设计及其边界并为不可信 spec 搭建解释器 受限 loader 的安全渲染方案。包定位一个组件两条渲染管线astryxdesign/vega在 packages/vega 目录下实现官方定位是 chart and data visualization components图表与数据可视化组件。它本身不重复造轮子而是通过 Vega 运行时渲染 Vega 与 Vega-Lite 规范Vega-Lite 规范组件检查$schema后调用vega-lite的compile()先编译为 Vega 规范再渲染Vega 规范跳过编译直接交给vega.parse()渲染无效或缺失$schema调用onError且不渲染任何内容。组件在 VegaChart.tsx 中完整呈现了这条管线先由parseSchema(spec.$schema)校验并判定库类型Vega-Lite 规范经compile(spec, compileOptions).spec编译随后parse(vegaSpec, parseConfig, parseOptions)得到 Runtime最后new View(runtime, {hover: true, ...viewOptions, container})构造视图——container始终由组件注入覆盖调用方传入的同名值。测试 VegaChart.test.tsx 亦验证了这一分支Vega-Lite spec 会且只会调用一次compile而原生 Vega spec 不会触碰 vega-lite 编译器。与 Astryx 其他组件不同本包不依赖 StyleX因此VegaChart不接受xstyleprop布局覆盖请使用className或style源码注释见 VegaChart.tsx。同时组件扩展了React.HTMLAttributesHTMLDivElement剔除contentEditable、dangerouslySetInnerHTML等可将任意 DOM 事件、data-testid、aria-label等透传到容器 div 上。安装只有canary没有latestVega 包的发布状态比较特殊它只以canarydist-tag 发布到 npm尚无 stablelatest版本因此安装时必须显式指定 tagnpm install astryxdesign/vegacanary vega vega-lite两个要点需要注意Peer 依赖需自行安装。vega、vega-lite以及react 19.2.0、react-dom 19.2.0是 package.json 中声明的 peerDependenciesvega 6.0.0、vega-lite 6.0.0。VegaChart 组件内部大量使用 React 19 的useEffectEvent见 VegaChart.tsx这是它要求 React 19.2 的直接原因。Canary 构建跟踪main分支最新提交版本形如0.x.y-canary.sha任意两个 canary 之间都可能发生破坏性变更——需要稳定性就锁死精确版本。canary-only 的机制由 package.json 中的astryx: {canaryOnly: true}与private: true双重标记保证详见下文构建与发布一节。快速上手两种 spec 的写法Vega-Lite 规范自动编译传一个带$schema的 Vega-Lite 顶层规范对象即可组件自动完成编译与渲染import {VegaChart} from astryxdesign/vega; VegaChart spec{{ $schema: https://vega.github.io/schema/vega-lite/v5.json, mark: bar, data: { values: [ {a: A, b: 28}, {a: B, b: 55}, ], }, encoding: { x: {field: a, type: ordinal}, y: {field: b, type: quantitative}, }, }} /;Vega 规范直接渲染不编译传原生 Vega 规范时vega-lite编译器不会被调用parse()直接消费原始 specVegaChart spec{{ $schema: https://vega.github.io/schema/vega/v5.json, marks: [...], }} /完整配置从 compile 到 View 的全链路透传VegaChart的设计哲学是把 Vega 的底层 API 原样暴露为 propsparseConfig、parseOptions对应vega.parse(spec, config, options)viewOptions对应new vega.View(runtime, options)compileOptions对应 Vega-Lite 的compile(spec, options)。下面的例子展示了全部透传点VegaChart spec{spec} parseConfig{{background: #1a1a1a}} parseOptions{{ast: true}} viewOptions{{ renderer: canvas, logLevel: 1, tooltip: myTooltipHandler, locale: myLocale, loader: myLoader, }} onReady{view { view.addSignalListener(highlight, (name, value) { console.log(signal:, name, value); }); }} onError{err console.error(Chart error:, err.message)} /Prop 总表Prop类型默认值说明specAnySpec--带$schema的 Vega 或 Vega-Lite 规范必填dataViewData--初始数据集{datasetName: tuples[]}compileOptionsCompileOptions--传给compile(spec, options)仅 Vega-Lite 生效parseConfigConfig--传给parse(spec, config)的 Vega 配置parseOptionsParseOptions--传给parse(spec, config, options)的选项viewOptionsOmitViewOptions, container--传给new View(runtime, options)的选项classNamestring--容器 div 的 CSS 类styleCSSProperties--容器 div 的内联样式onReady(view: View) void--View 就绪后回调携带活的 Vega ViewonError(err: Error) void--schema 错误、编译失败或渲染失败时回调注意AnySpec (VegaSpec | VegaLiteSpec) {$schema: string}见 types.ts——$schema是类型层面的硬性要求这与运行时校验一致。viewOptionsView 构造选项直接映射到 Vega 的ViewOptionscontainer被剔除始终由组件注入。常用字段字段类型说明renderersvg \| canvas渲染后端默认svghoverboolean启用 hover 编码默认truelogLevelnumberVega 日志详细程度loggerLoggerInterface自定义 loggertooltipTooltipHandler自定义 tooltip 处理器localeLocaleFormatters数字与时间格式化 localeloaderLoader自定义数据 loaderbackgroundColor图表背景色源码中hover: true是组件给出的默认值随后被viewOptions展开覆盖VegaChart.tsx测试 VegaChart.test.tsx 验证了viewOptions{{hover: false}}能正确覆盖默认值同时container仍由组件注入。compileOptions仅 Vega-Lite 生效字段类型说明configVegaLiteConfig在 spec 自带 config 之上合并的 Vega-Lite 配置loggerLoggerInterface编译期间使用的自定义 loggerfieldTitle(fieldDef, config) string自定义字段标题格式化器这套类型并未直接借用 vega-lite 的公共导出其CompileOptions不在公共 API 面内而是在 types.ts 中重新声明并有意将fieldTitle定义为宽松签名以避免耦合 vega-lite 未导出的内部类型。compileOptions对原生 Vega spec 被直接忽略。parseOptionsAST 保留字段类型说明astboolean在运行时保留表达式 AST默认false对工具链与解释器模式有用ast: true是不受信任 spec安全方案的前置条件之一见下文因为保留 AST 后表达式不再用Function构造函数编译。View 生命周期按值而非按引用重建VegaChart在挂载时构建 VegaView卸载时view.finalize()释放资源。两次挂载之间仅当spec、compileOptions、parseConfig、parseOptions、viewOptions中某个的值发生变化时才重建 View——比较基准是 View 构建时保存的一份值快照而不是上一次的 props。这带来两个直接推论且两者都无需useMemo每次渲染内联重建的对象字面量不会拆掉图表在 ref、模块常量或跨渲染共享对象上原地修改spec会被检测到——因为比较对象是构建时的副本而非上一次 props。背后的实现是 viewInputs.ts 中的 latch 机制latchViewInputs()保存{inputs, snapshot}其中snapshot是运行时依赖 props 的结构副本viewInputs.tslatchIsCurrent()用快照逐项比对当前值viewInputs.ts。为什么必须与副本比对而不能与上一次 props比对因为原地修改会让新旧 props 指向同一个对象——旧值已丢失仅比较引用会漏掉变更导致图表静默过期源码注释明确点出这一点viewInputs.ts。快照比对在渲染期间执行因此判定必须稳定VegaChart.test.tsx 覆盖了这些场景相等值重渲染保持单 View、renderer: canvas → svg触发重建、函数引用变化触发重建、共享嵌套对象被原地修改后重渲染会重建且第二次编译结果反映新字段值。比较的粒度结构值 vs 引用值普通对象与数组逐项比较内联重建的等价 options 视为未变函数tooltip、logger、loader、expr、fieldTitle与类实例按引用比较两个外表相同的实例行为可能不同副本无法捕获其方法语义——所以应替换值而非原地修改。两种无法复制比较的形状快照并非无限深拷贝它设置了MAX_DEPTH 100深度上限并用OPAQUE符号标记无法遍历的子树viewInputs.ts。有两种形状会退化为按引用比较引用环子树重新进入自身路径上的对象环的回边指向副本已遍历过的对象因此环不会隐藏任何变更循环 spec 任意位置的原地修改依然能被检测嵌套超过 100 层深度以下的原地修改不可见引用没变、值没被复制需要替换对象或通过onReady驱动 View 更新。无论哪种情况凡是被复制到的部分仍按值比较因此 spec 中其他位置的变更照样被捕获。测试 viewInputs.test.ts 对相同值新字面量、键增删、循环 spec 挂载/重渲染/替换、深度嵌套等边界均有断言。这套opaque 按引用比较设计还有一层防呆作用若无法复制部分每次都比较为不等渲染期刷新 latch 将导致 React 无限重渲染Too many re-renders按引用比较则保证同对象稳定、换对象恰好重建一次。不触发重建的 propsdata、className、style、onReady、onError对生命周期完全惰性永不单独触发 View 重建。onReady与onError通过useEffectEvent实现回调永远读取最新 props又不会成为 Effect 的响应式依赖父组件每次渲染传新内联函数也不会拆掉图表VegaChart.tsx。数据加载data只做初始化动态更新请走onReadydata将数据集名映射到元组数组在 View 初始化阶段、首次渲染之前通过view.data(name, tuples)应用VegaChart.tsx。它是非响应式的挂载后修改被忽略仅靠新data对象本身永远不会重建 View当其他因素重建 View 时新 View 会加载那一刻data持有的值。测试 VegaChart.test.tsx 明确断言仅data变化时 View 不重建、view.data保持只调用一次且传给 Vega 的是初始旧值而 spec 变化触发重建时新 View 加载最新 dataVegaChart.test.tsx。要在渲染后动态更新数据用onReady拿住活的 View 自行驱动VegaChart spec{spec} data{{ table: [ {category: A, value: 28}, {category: B, value: 55}, ], }} onReady{view { // 之后随时更新数据 view.data(table, newRows); view.runAsync(); }} /ViewData的类型定义为Recordstring, unknown[]types.ts每个 key 必须匹配 specdata数组中定义的数据集名。安全边界处理不受信任的 specVega/Vega-Lite spec是程序而非纯数据Vega 会求值 spec 内的表达式字符串signals、事件流、encodings、filters默认用Function构造函数将其编译为 JavaScriptspec 的data项还能命名 URL——包括由 signal 动态构造的 URL——默认 loader 会用页面凭据去抓取。VegaChart以 Vega 默认配置渲染传入的 spec因此默认值就是信任边界只传自己编写或审阅过的 spec。若 spec 来自用户输入、持久化文档或模型输出请通过既有的透传选项接上 Vega 官方的安全求值推荐配置import {expressionInterpreter} from vega-interpreter; import {loader} from vega; VegaChart spec{untrustedSpec} // 保留表达式 AST并以解释方式求值不用 Function 构造函数。 // 更慢且有一小部分表达式不受支持——参见 vega-interpreter 文档。 parseOptions{{ast: true}} viewOptions{{ expr: expressionInterpreter, // 限制或禁用spec 可加载的内容。 // mode: file 且不带 baseURL 时拒绝一切加载 // 要按域名白名单放行请传入自定义 loader。 loader: loader({mode: file}), }} /;vega-interpreter是独立包npm install vega-interpreter同时建议配合省略unsafe-eval的 Content-Security-Policy让平台层而非仅配置层强制这条安全边界。在 types.ts 的spec文档中这组配置被标注为必需而非可选。Schema 校验与parseSchema工具VegaChart在开始任何工作前先校验spec.$schema以下情况会调用onError且不渲染$schema缺失或非字符串URL 不匹配官方格式schema/{library}/{version}.json库名不是vega或vega-lite。校验实现位于 schema.ts正则SCHEMA_RE /schema\/([\w-])\/([\w.-])\.json$/匹配 schema 路径段且容忍任意代理前缀任何schema/之前的内容均可。parseSchema也从包的 barrel 入口 index.ts 作为公共工具导出返回成功{ok: true, library: vega | vega-lite, version: string}失败{ok: false, error: string}URL 缺失、格式错误或库未知。测试 schema.test.ts 给出了完整的行为矩阵可解析vega-lite/v5、vega/v6、多段版本号v5.2.0容忍代理前缀https://internal-proxy.example.com/assets/vega.github.io/schema/vega/v5.json拒绝缺失并附带修复提示文案、非字符串报出实际类型、格式不符与未知库如vega-embed。额外导出Astryx 主题化的 Vega-Lite 配置除VegaChart与parseSchema外index.ts 还导出了主题配置工具与常量均源自 vegaLiteConfig.tsbuildVegaLiteConfig(token)接收一个 CSS 自定义属性解析函数如useXDSTheme()返回的token返回一份以 Astryx token 主题化的 Vega-LiteConfig涵盖 axis 样式、图例布局、线/点 mark 默认值、标题排版与视图 chrome——颜色标度由range结合数据可视化 token 设置常量DEFAULT_STROKE_WIDTH线宽 2、DEFAULT_POINT_SIZE点尺寸 64、DEFAULT_LEGEND_ORIENTright、LEGEND_OFFSET16、TITLE_OFFSET16。典型用法是在可访问主题的组件内调用const config buildVegaLiteConfig(token)然后作为compileOptions.config或直接并入 spec 的 config。构建使用 pnpm workspace 命令构建tsup 产出 CJS ESM随后tsc仅发声明文件pnpm -F astryxdesign/vega buildtsup.config.ts 定义src/index.ts为入口产出cjs与esm两种格式并将react、react-dom、vega、vega-lite声明为 external不打包进产物由使用方 peer 依赖提供。产物映射在 package.json 中main指向dist/index.jsmodule指向dist/index.mjstypes指向dist/index.d.ts。发布机制canary 自动发布与 stable 毕业流程为什么当前只有 canarypackage.json 保持private: true并带有astryx: {canaryOnly: true}标记二者共同约束发布行为。发布工作流 .github/workflows/release.yml 同时处理两个 dist-tagstablelatestjob跳过所有private或canaryOnly的包!p.private !p.astryx?.canaryOnly才可发布因此 Vega 永远不可能被误发布为 stable 版本canary job每次 push 到main时触发。仅在临时 CI 检出中绝不写回 git为canaryOnly包剥离private标记并以0.x.y-canary.short-sha版本、--tag canary --provenance --access public发布配合 npm OIDC trusted publishing 与 provenance 供应链凭证。提交在仓库中的private: true是 npm 层面不可能发生 stable 发布的硬保证——在有意执行下述毕业步骤之前不要移除它。首次 canary 的前置引导第一个 canary 在包名被 npm 认领之前不会发布npm 无法为一个尚不存在的名字注册 OIDC 信任。需要astryxdesignnpm org 的 owner 做一次引导首次 stable 发布前同样适用npm i -g npmlatest npm login --registry https://registry.npmjs.org # 必须是 astryxdesign org owner pnpm run setup-trusted-publishing # 审计——显示哪些需要 bootstrap/trust pnpm run setup-trusted-publishing --bootstrap --setup-trust --workflow release.yml这会发布一个 deprecated 的0.0.0-bootstrap.0桩版本以认领包名并把release.yml注册为受信任发布者。在完成之前CI 对该包的 canary 发布会失败。毕业为公开 stablelatest发布当 Vega 准备好公开发布 stable 版本时按顺序执行以下步骤与其他astryxdesign/*公开包的发布流程一致移除 canary-only 限制编辑packages/vega/package.json删除private: true删除astryx: {canaryOnly: true}块。加入版本组在.changeset/config.json的fixed数组中加入astryxdesign/vega使其与其他可发布包协同版本号统一升到同一版本并将version设为其他包当前的已发布版本。确认包名已在 npm 认领并建立信任即上文引导步骤未认领/未信任的名字在 stable 发布时与 canary 一样会失败。添加 changeset让发布说明与版本号包含 Vegapnpm changeset:new落地变更然后走常规发布流程完成版本号提升与发布合并移除限制的 PR该 push 到main会自动触发一次 canary 发布运行版本提升 PRpnpm version-packages刷新 lockfile 后合并——它会在main上提升版本号但不发布任何东西手动派发 stable Release 工作流发布latestdist-taggh workflow run release.yml --ref main -f dry-runtrue # 可选预览 gh workflow run release.yml --ref main # 发布 latest gh run list --workflowrelease.yml -L 3 # 观察进度发布过程无 tokennpm OIDC trusted publishing且版本门控 幂等——重复运行是安全的无需手写npm publish也无需 npm token。stable 发布完成后npm install astryxdesign/vega不带 tag将解析到 stable 版本canarytag 则继续跟踪main。源码结构与测试覆盖包内文件布局见 packages/vega文件角色用途src/index.tsBarrel公共 API 出口VegaChart、parseSchema、buildVegaLiteConfig与主题常量src/VegaChart.tsx组件检查$schema、编译或直渲、拥有 View 生命周期src/viewInputs.ts工具检测拥有 View 的 props 的值变化latch/快照src/schema.ts工具解析并校验 Vega/Vega-Lite$schemaURLsrc/types.ts类型本包共享的 TypeScript 类型src/vegaLiteConfig.ts工具Astryx 主题化的 Vega-Lite 配置构建器src/VegaChart.test.tsx测试View 生命周期与错误契约的功能测试src/schema.test.ts测试$schemaURL 解析器测试src/viewInputs.test.ts测试变更检测器单元测试测试采用 vitest共三个测试文件覆盖三大核心契约spec 分支与透传compile/parse 参数逐一断言、View 重建策略值比较、引用比较、循环/超深退化、data 非响应式与错误契约$schema非法时既不 compile 也不 parse、不构造 View仅渲染空容器 div编译期同步异常与runAsync拒绝均路由到onError非 Error 拒绝值会被包装为Error卸载后迟到的拒绝不再触发回调。这些测试既是行为的权威定义也是你在集成 VegaChart 时最值得参考的使用边界文档。【免费下载链接】astryxAn open source design system thats fully customizable and agent ready项目地址: https://gitcode.com/GitHub_Trending/as/astryx创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考