04-05:React Flow 与布局调试全记录¶
概述¶
04-05 这天集中解决了两个布局问题:React Flow 流程图的 dagre 布局异常(画布不显示/节点重叠),以及侧边栏机器人列表的固定列数问题。两者的共同主题是「布局问题的根因往往不在表层,而在数据输入」。
第一部分:React Flow + Dagre 布局调试¶
前置:先修数据,再做图¶
[13:13] Dad 决策:当前数据层级设计有问题,中间层"开发/运维/健康"意义不大。第二层不一定是 project,也可以是 topic。先修数据,再做 React Flow。
数据修复结果:
- 108 条 topic 全部重新分类
- 33 条发生变化,42 条进一步修正
- Agent Portal 从 42 条降到 30 条
- 最终形成 17 个项目组
首次接入 React Flow — 画布不显示¶
[13:46] 安装 React Flow + dagre,派 Codex 做改造。
[13:59] 验收结果:头部统计正常(10 个进行中 / 108 个主题 / 10 个分类),但画布空白。
排查过程:
| 步骤 | 检查内容 | 结果 |
|---|---|---|
| 1 | 容器高度 | ✅ 有高度,约 600px(h-[calc(100vh-18rem)]) |
| 2 | 节点数量 | ✅ 154 个节点存在 |
| 3 | 节点坐标 | ❌ 第一个节点 y=10579px,极端偏移 |
| 4 | fitView | ⚠️ 在工作,但整体图过大 |
初步结论:不是"没有节点",而是 dagre 把节点排成超长纵向结构。
fitView 失效的排查¶
降低 minZoom 到 0.05 后能看到一个节点,但缩放极小。说明 fitView 基于正确的 bounds 计算,但布局本身异常。
尝试过的修复:
1. onInit 手动 fitView — 无效
2. useEffect + requestAnimationFrame — 无效
3. 延长触发延时 — 无效
4. ReactFlowProvider + AutoFitView 子组件 — 无效
5. 切换到"进行中"筛选(10 个 topic / 4 个分类)— 部分可见,但仍重叠
关键判断:fitView 失效是结果不是根因。布局坐标本身错了,fitView 再怎么调都没用。
节点重叠 — 收敛到 dagre 输出异常¶
[14:08] 打印坐标发现:18 个节点只有 4 个不同坐标。进一步检查 dagre 直接输出:
dagre 完全没有把节点垂直分散开。排除了节点 ID 冲突(ID 形如 project:${category}:${project},理论上唯一)。
根因定位 — dagre 节点尺寸对象引用复用¶
发现:getNodeSize 返回的是 NODE_SIZE[type] 的对象引用。dagre 的 setNode 需要独立的 { width, height } 对象。共享引用导致 dagre 内部修改污染所有同类型节点。
错误模式:
修复:
function getNodeSize(type) {
const size = NODE_SIZE[type]
return { width: size.width, height: size.height } // 返回新对象
}
结果:流程图渲染正确,三层结构(Category → Project → Topic)清晰可见。
问题与解决方案对照¶
| 表象 | 实际原因 | 解决 |
|---|---|---|
| 画布不显示 | 节点堆叠在少数坐标,视觉空白 | 修复 dagre 尺寸输入 |
| fitView 失效 | 布局结果本身错误 | 先修布局,再 fitView |
| 节点重叠 | NODE_SIZE[type] 对象引用被复用 |
每次返回新 {width,height} |
第二部分:侧边栏自适应网格布局¶
问题¶
[14:19] Dad:左侧机器人列表的列数应该自动变化,当前固定布局太窄了。
现状:使用 Tailwind 固定断点列数:
问题原因:这些断点基于 viewport 而不是 sidebar container width。侧边栏不是全屏容器,viewport 断点不适用。
解决方案¶
改为 CSS Grid 自适应列定义:
auto-fill:根据容器宽度自动填充列数minmax(72px, 1fr):每列最小 72px,有空间则扩展
[14:20] 修改并部署完成。侧栏窄时自动减少列数,宽时自动增加。
历史演进¶
| 时间 | 事件 |
|---|---|
| 03-21 | Dad 首次提出:拖拽宽度后格子数应自适应,默认 4 格 |
| 03-21 | 首版网格上线,发现窄 sidebar(288px)下 minmax(68px) 退化为 1 列 |
| 03-23 | Dad 设计审查:有空格,应该自适应 |
| 03-26 | 分支合并时从 auto-fill 回退到固定 grid-cols-*(临时处理) |
| 04-05 | 最终修复:恢复 auto-fill + minmax(72px, 1fr) |
调试方法论总结¶
从整个布局调试过程中沉淀的排查清单:
React Flow 画布不显示¶
- 检查父容器是否有明确高度
- 检查
nodes.length > 0 - 打印节点坐标,看是否落在极端范围
- 检查布局引擎输出是否合理
- 先排除缓存/旧 bundle/Service Worker
fitView 失效¶
- 先打印节点坐标范围,确认布局结果是否正确
- 检查
minZoom是否过大 - 布局错误时,修布局而不是调 fitView
dagre 节点重叠¶
- 检查
setNode的width/height是否正确 - 检查是否复用了共享对象引用(最常见根因)
- 检查节点 ID 是否唯一
- 直接打印 dagre 输出坐标
核心经验¶
不要把布局问题当视觉问题。React Flow "不显示"很多时候不是 CSS,而是布局坐标已经坏了。 fitView 失效通常是结果,不是根因。 dagre 最怕共享可变对象 —
return { ...NODE_SIZE[type] }是关键。
里程碑¶
- 04-05 13:18 — Dad 决定先修数据再做 React Flow
- 04-05 13:44 — 108 条 topic 数据修复完成
- 04-05 13:59 — React Flow 首次接入,确认画布不显示是布局问题
- 04-05 14:08 — 定位根因:dagre 节点尺寸对象引用复用。修复后流程图正常渲染
- 04-05 14:20 — 侧边栏改为 auto-fill + minmax 自适应布局