现象
仪表盘的柱状图 widget 首帧不渲染柱子:坐标轴、分类标签、Y 轴刻度都在,但一根柱子都没有,等 8s 也不出;任何一次重渲染(切主题 / 改窗口大小 / 重新进页面)才让柱子从 0 长到正确高度。数据完全正确 —— 纯前端挂载期渲染竞态。
#2727 的修复(settle 重挂)已合并,但线上无效 —— 本条即为跟进。
复现
在最新 main 上跑 AI 魔法流程(cloud 8f56643 / framework a79120010 / objectui a3b0ca861),AI 搭一个带示例数据的 CRM,首页仪表盘一个 bar widget(dimension=status,value=count):
- 首次进仪表盘页 → 图表有坐标轴 + 分类标签(合作中/已流失/潜在)+ 0–8 Count 刻度,但无柱子,等 8s 无变化,无 console 报错。
- 切一次主题(或任意重渲染)→ 柱子出现并从 0 动画到正确高度(5/3/4)。
- 重新导航回仪表盘 → 首帧又是空的。稳定复现。
数据路径正常:POST /api/v1/analytics/dataset/query 返回 200,rows:[{status:合作中,count:5},{status:已流失,count:3},{status:潜在,count:4}],sql: SELECT status, COUNT(*) ... GROUP BY status。
#2727 确实在服务端 bundle 里(不是 stale dist)
rig 服务的是 framework vendored console(framework/packages/console/dist),其戳 dist/.objectui-sha = 69fa5d163a97…,今天重建,且 69fa5d163包含 #2727 的合并提交 bef2aaeb7(git merge-base --is-ancestor bef2aaeb7 69fa5d163 = true)。check-console-sha 也 pass(根 pin 与 dist 戳一致)。所以问题不是"没打进包",而是修复本身在真实浏览器里没起作用。
这也对上了 #2727 那次修复自己的备注:它从没在真实浏览器里目视验证过,只在 happy-dom + 可控 ResizeObserver 下跑了单测。
根因猜测
ChartContainerImpl.tsx 的 settle-remount 逻辑是"等尺寸变化停止在一个正尺寸(80ms debounce)后 bump 一次 settleNonce → 作为 <ResponsiveContainer key={settleNonce}> 触发一次干净重挂"。
但在仪表盘里,react-grid-layout 往往在第一次 ResizeObserver 回调就给了 widget 一个确定的正尺寸 —— 没有 0→正 的尺寸变化可"settle from"。于是"等变化停止"这一支要么从不触发、要么在动画已经卡死在 0 之后才(不)触发 → settleNonce 永不 bump → 不重挂 → recharts 进场动画维持在 height 0。也就是修复赌的那个"尺寸会先抖动再稳定"的信号,在 grid 场景下不成立。
(recharts 侧:JavascriptAnimate 用 useState(isActive ? 0 : 1) 起始 0,靠 rAF tween 长上去;被 grid 挂载抖动打断后卡在 0,只有换 key 重挂才重播 —— 这部分 #2727 分析正确,只是触发重挂的条件没覆盖到 grid 首帧。)
建议方向(择一)
- 挂载后无条件强制重挂一次(一个 rAF / next-tick 后 bump
settleNonce,不依赖"尺寸变化 settle"信号),happy-dom 里可用 0×0 门槛跳过以不影响单测; - 或对仪表盘图表直接
isAnimationActive={false}(首帧即最终高度,牺牲进场动画换正确性); - 或把渲染 gate 在"容器有正尺寸 且 已过一个 paint 帧"上再挂 recharts。
务必这次要在真实浏览器 / 部署环境目视验证(rig 服务 framework vendored console,单测的 happy-dom 复现不了这个 grid 时序)。
影响
用户搭完应用第一眼看到的就是空仪表盘 —— 魔法时刻的直接体验损伤。覆盖所有走 ChartContainer 的仪表盘图表路径。
关联:#2727(本条的失败修复);后端数据侧无问题。
现象
仪表盘的柱状图 widget 首帧不渲染柱子:坐标轴、分类标签、Y 轴刻度都在,但一根柱子都没有,等 8s 也不出;任何一次重渲染(切主题 / 改窗口大小 / 重新进页面)才让柱子从 0 长到正确高度。数据完全正确 —— 纯前端挂载期渲染竞态。
#2727 的修复(settle 重挂)已合并,但线上无效 —— 本条即为跟进。
复现
在最新 main 上跑 AI 魔法流程(cloud
8f56643/ frameworka79120010/ objectuia3b0ca861),AI 搭一个带示例数据的 CRM,首页仪表盘一个barwidget(dimension=status,value=count):数据路径正常:
POST /api/v1/analytics/dataset/query返回 200,rows:[{status:合作中,count:5},{status:已流失,count:3},{status:潜在,count:4}],sql: SELECT status, COUNT(*) ... GROUP BY status。#2727 确实在服务端 bundle 里(不是 stale dist)
rig 服务的是 framework vendored console(
framework/packages/console/dist),其戳dist/.objectui-sha = 69fa5d163a97…,今天重建,且69fa5d163包含 #2727 的合并提交bef2aaeb7(git merge-base --is-ancestor bef2aaeb7 69fa5d163= true)。check-console-sha也 pass(根 pin 与 dist 戳一致)。所以问题不是"没打进包",而是修复本身在真实浏览器里没起作用。这也对上了 #2727 那次修复自己的备注:它从没在真实浏览器里目视验证过,只在 happy-dom + 可控 ResizeObserver 下跑了单测。
根因猜测
ChartContainerImpl.tsx的 settle-remount 逻辑是"等尺寸变化停止在一个正尺寸(80ms debounce)后 bump 一次settleNonce→ 作为<ResponsiveContainer key={settleNonce}>触发一次干净重挂"。但在仪表盘里,
react-grid-layout往往在第一次 ResizeObserver 回调就给了 widget 一个确定的正尺寸 —— 没有 0→正 的尺寸变化可"settle from"。于是"等变化停止"这一支要么从不触发、要么在动画已经卡死在 0 之后才(不)触发 →settleNonce永不 bump → 不重挂 → recharts 进场动画维持在 height 0。也就是修复赌的那个"尺寸会先抖动再稳定"的信号,在 grid 场景下不成立。(recharts 侧:
JavascriptAnimate用useState(isActive ? 0 : 1)起始 0,靠 rAF tween 长上去;被 grid 挂载抖动打断后卡在 0,只有换key重挂才重播 —— 这部分 #2727 分析正确,只是触发重挂的条件没覆盖到 grid 首帧。)建议方向(择一)
settleNonce,不依赖"尺寸变化 settle"信号),happy-dom 里可用 0×0 门槛跳过以不影响单测;isAnimationActive={false}(首帧即最终高度,牺牲进场动画换正确性);务必这次要在真实浏览器 / 部署环境目视验证(rig 服务 framework vendored console,单测的 happy-dom 复现不了这个 grid 时序)。
影响
用户搭完应用第一眼看到的就是空仪表盘 —— 魔法时刻的直接体验损伤。覆盖所有走
ChartContainer的仪表盘图表路径。关联:#2727(本条的失败修复);后端数据侧无问题。