关于 bklit-studio-chart-performance
面向 bklit-ui 贡献者的图表性能优化 Skill,适用于 Studio 中交互卡顿的图表。它提供渲染隔离、静态 SVG 路径、上下文拆分和验证步骤。
主要功能
- 01定位交互引发的重复渲染
- 02拆分稳定数据与悬停状态
- 03完成进入动画后静态化路径
- 04缓存图表上下文与预览数据
- 05提供 Studio 性能验证清单
适用场景
- 01排查滑块操作导致的图表卡顿
- 02减少图例悬停时的重复计算
- 03优化画布平移期间的渲染
- 04发布前检查图表交互性能
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ bklit/ bklit-ui --skill bklit-studio-chart-performance安装后在 Claude Code 中输入/bklit-studio-chart-performance确认已加载。
详细介绍
Studio 图表性能
当图表在 Studio 中显得卡顿,但类似图表(例如饼图)运行正常时使用。
一句话规则
如果需要入场动画,就保留路径上的入场动画,然后移除 Motion 路径订阅并隔离悬停状态,这样 Studio 滑块和图例更新就不会每帧都在所有系列上重复执行昂贵的弧线路径计算。
1. 找出每次交互都会重新渲染的内容
Studio 会在每次滑块刻度变化以及悬停图例/扇区时更新 displayState。追踪以下内容:
- 悬停状态是否与数据、比例尺和动画配置位于同一个上下文中?
- 预览是否会在每次渲染时重新创建子项(
data.map、图案 defs、Motion props)? - 图表是否会不必要地重新挂载(
key关联的是 Motion 签名还是手动重播)?
模式:像笛卡尔图表/饼图那样拆分上下文——稳定切片(数据、几何信息、动画配置)与悬停切片(hoveredIndex、工具提示)。不需要悬停状态的使用者只使用稳定 hook(usePieStable、useRingStable、useChartStable 等)。
**Studio 平移:**将图表渲染包在 StudioChartRender(packages/studio/src/components/studio-chart-render.tsx)中,这样在渲染 props 未变化时,相机平移/FPS 计数器父组件的更新会跳过图表树。
2. 将 SVG 路径 d 动画视为高开销操作
使用 Motion / useTransform 与 d3 arc(或类似方案)对 d 添加动画时,每帧、每个系列都会运行布局和绘制。
| 优先选择 | 避免 |
|---|---|
悬停时使用 transform / opacity(利于合成器处理) | 入场动画完成后持续进行 d 变形 |
入场动画结束后保持 d 静态 | 图表整个生命周期都保留 d 上的 Motion 订阅 |
| 仅使用入场动画,之后保持路径静态 | 因无关 prop 变化而重新运行入场路径计算 |
模式:使用 useMountProgress 实现入场动画 → 当进度 ≥ 1(useEnterComplete)时,渲染静态路径,并仅通过 motion.g 包装器上的 x/y/opacity/scale 为悬停添加动画(不要在 motion.path 上逐路径设置 scale)。
共享 hook:packages/ui/src/charts/use-enter-complete.ts
3. 对图表外壳上下文进行记忆化
未记忆化的 provider 值会迫使所有子项在每次父级渲染时都进行协调。
- 使用显式依赖项(数据、弧线/半径、尺寸、回调)对稳定上下文对象进行记忆化。
- 根据
hoveredIndex和稳定的setHoveredIndex对悬停上下文进行记忆化(在图表外壳中使用useCallback)。 - 让
isLoaded与环形/笛卡尔图表保持一致:使用useEffect+ timeout,而不是延迟的useState初始化器。
参考:pie-context.tsx、ring-context.tsx、chart-context.tsx、PieChartCore / RingChartCore 中 provider 值上的 useMemo。
4. 针对 Studio 预览的优化
与图表类型无关;应用于 packages/studio/src/components/charts/*-studio*.tsx:
| 优化 | 方法 |
|---|---|
| 条件式 defs | 仅当某个系列使用图案时才传入 patternDefs / gradients |
| 对派生数据进行记忆化 | 彩色/映射后的数据数组;扇区/系列列表(useMemo,依赖项:dataSeed + 影响颜色的设计字段) |
| 对入场 Motion 进行记忆化 | 不要内联调用 getStudioMotionEnterProps;使用 useMemo,依赖项仅限 Motion 相关项(不要使用完整的 state) |
| 对图例悬停进行记忆化 | 在 useMemo 中使用 { hoveredIndex, setHoveredIndex }——已在 studio-legend-hover.tsx 中实现 |
| 对图表主体进行记忆化 | 使用 memo() 包装器;传入基本类型值(chartKey、chartSize、data),而非整个 ctx,这样平移/外壳重新渲染时就会跳过重建 |
| 在 Studio 中禁用辉光 | 在系列组件上设置 showGlow={false} |
参考:pie-studio-preview.tsx、ring-studio-preview.tsx
5. 在 Studio 中与“快速”同类图表进行比较
将卡顿的图表与同一编辑器中操作流畅的图表(通常是 pie-chart)进行对比:
| 检查项 | 卡顿的图表通常有 | 流畅的图表通常有 |
|---|---|---|
| 外壳 | 内联渲染、额外的定义 | StudioChartShell + 条件模式 |
| 系列数量 | 大量动画路径 | 更少的路径或更简单的几何形状 |
| 悬停 | Context + 整个树重新渲染 | 稳定的订阅者;在 motion.g 上悬停 / 使用 translate |
| 入场 | 每个系列的路径 d 擦除动画 | 入场后 d 静态;悬停时仅使用变换 |
| 平移 | 每帧重建图表树 | StudioChartRender 记忆化边界 |
6. 验证标准
创建 PR 前:
pnpm lint
pnpm --filter @bklitui/ui check-types
pnpm --filter @bklitui/studio check-types
# scoped production build when touching studio/web
手动访问 /studio?chart=<slug>:
- 入场动画
- 悬停 / 图例同步
- 拖动几何形状滑块(不必要时不重新挂载)
- 入场后在画布上平移(按住空格键 + 拖动)——FPS 接近 pie-chart 基准
- 支持时使用图案 / 渐变模式
图表状态(bklit-ui)
| 图表 | Slug | 状态 |
|---|---|---|
| Pie | pie-chart | ✅ 参考(#120) |
| Ring | ring-chart | ✅ 符合检查清单(拆分 context、静态路径、StudioChartRender、预览记忆化) |
| Radar / Funnel / Choropleth | various | 部分完成 — 运行检查清单 |
| Cartesian / scatter / live-line | various | ✅ #91 抽稀 + 悬停批处理 |
| Sankey | sankey-chart | 在链接描边上使用 useTransform — 可考虑使用 useEnterComplete |
| Gauge | gauge-chart | 优先级较低(单个弧线) |
关键文件
| 区域 | 路径 |
|---|---|
| 入场完成钩子 | packages/ui/src/charts/use-enter-complete.ts |
| Pie 参考 | packages/ui/src/charts/pie-slice.tsx、pie-context.tsx、pie-studio-preview.tsx |
| Ring | packages/ui/src/charts/ring.tsx、ring-chart.tsx、ring-studio-preview.tsx |
| 平移隔离 | packages/studio/src/components/studio-chart-render.tsx |
| 注册表 | packages/studio/src/lib/registry.tsx |