SuperAPI AI 工具集

dagre-react-flow

前端与建站

查看项目

更新于 10-15 16:20

技能介绍

概述

dagre-react-flow 是一种在 React Flow(@xyflow/react)中实现有向图自动布局的实践方案。其分工明确:dagre 是一个用于有向图布局的 JavaScript 库,负责计算层级/树形结构中节点的最优位置;React Flow 负责图形的渲染与交互。二者结合可实现自动布局、层级布局、树形结构与节点排列。

安装

pnpm add @dagrejs/dagre

主要能力

  • 使用 dagre 计算节点坐标,再由 React Flow 渲染。
  • 提供 getLayoutedElements 布局函数,支持可选参数:direction('TB' | 'BT' | 'LR' | 'RL')、nodeWidth、nodeHeight、nodesep(水平间距)、ranksep(垂直/层间距)。
  • 提供 useAutoLayout Hook,可在节点测量初始化后自动执行一次布局,并返回 runLayout 供手动重新布局。
  • 支持通过边的 weight(越高路径越直接)与 minlen(最小层级间隔)控制边路由。

使用方式

核心:布局函数

import dagre from '@dagrejs/dagre';
import type { Node, Edge } from '@xyflow/react';

export function getLayoutedElements(
  nodes: Node[],
  edges: Edge[],
  options = {}
) {
  const { direction = 'TB', nodeWidth = 172, nodeHeight = 36, nodesep = 50, ranksep = 50 } = options;

  const g = new dagre.graphlib.Graph();
  g.setGraph({ rankdir: direction, nodesep, ranksep });
  g.setDefaultEdgeLabel(() => ({}));

  nodes.forEach((node) => {
    const width = node.measured?.width ?? nodeWidth;
    const height = node.measured?.height ?? nodeHeight;
    g.setNode(node.id, { width, height });
  });

  edges.forEach((edge) => {
    g.setEdge(edge.source, edge.target);
  });

  dagre.layout(g);

  const layoutedNodes = nodes.map((node) => {
    const pos = g.node(node.id);
    const width = node.measured?.width ?? nodeWidth;
    const height = node.measured?.height ?? nodeHeight;
    return { ...node, position: { x: pos.x - width / 2, y: pos.y - height / 2 } };
  });

  return { nodes: layoutedNodes, edges };
}

关键:坐标系差异

dagre 返回的是节点中心坐标,React Flow 期望左上角坐标,因此需要换算:

const rfPosition = {
  x: dagrePos.x - nodeWidth / 2,
  y: dagrePos.y - nodeHeight / 2,
};

节点尺寸的三种来源

  1. 固定尺寸:g.setNode(node.id, { width: 172, height: 36 })。
  2. 来自节点数据的尺寸:g.setNode(node.id, { width: node.data.width ?? 172, height: node.data.height ?? 36 })。
  3. 实测尺寸(最准确):使用 node.measured?.width / node.measured?.height,缺省回退到默认值。

布局方向

值 方向 典型用途
TB 从上到下 组织图、决策树
BT 从下到上 依赖图(依赖在底部)
LR 从左到右 时间线、水平流程
RL 从右到左 RTL 布局

设置方式:g.setGraph({ rankdir: 'LR' })。

React Flow 集成

使用 useNodesState / useEdgesState 管理状态,通过 useReactFlow 的 fitView 在布局后适配视图:

setNodes([...newNodes]);
setEdges([...newEdges]);
window.requestAnimationFrame(() => {
  fitView({ duration: 300 });
});

需要在外层包裹 ReactFlowProvider。

useAutoLayout Hook

在 useNodesInitialized() 为真时执行一次布局,使用 useRef 防止重复应用;内部通过 getNodes / getEdges / setNodes / fitView 操作,并在下一帧调用 fitView。返回 { runLayout } 供手动触发。

边选项

edges.forEach((edge) => {
  g.setEdge(edge.source, edge.target, {
    weight: edge.data?.priority ?? 1,
    minlen: edge.data?.minRanks ?? 1,
  });
});

weight 越高,该边越倾向于更短、更直接的路径;minlen 强制两个相连节点之间至少间隔若干层级,例如 g.setEdge('a', 'b', { minlen: 2 })。

常用模式

  • 根据布局方向调整 Handle 位置:水平布局使用 Left/Right,垂直布局使用 Top/Bottom。
  • 动画过渡:为 .react-flow__node 添加 CSS transition(如 transform 300ms ease-out)。
  • 节点分组:将 type 为 'group' 的节点从 dagre 布局中排除,布局完常规节点后再合并回去。

验证要点

文档给出若干硬性校验步骤:

  1. 尺寸一致——传给 g.setNode 的宽高与用于计算 position 的宽高必须相同(半宽/半高需匹配 dagre 的节点框)。
  2. 中心转左上角——position 必须为 { x: centerX - width/2, y: centerY - height/2 },不能直接用原始中心坐标。
  3. 状态更新——布局后 setNodes / setEdges 必须收到新数组实例(如 [...layouted]),而非原引用。
  4. 可选的合理性检查——若布局后使用 fitView,应在节点提交后(如下一帧 requestAnimationFrame 或 setTimeout(0))执行,而不是与 setNodes 处于同一同步周期且使用过期测量值。

适用场景

  • 需要自动布局的有向图、层级图、树形结构。
  • 组织图、决策树、依赖图、时间线、水平/垂直流程图,以及 RTL 布局。
  • 需要按方向切换、按优先级控制路径走向的复杂节点图。

限制与注意事项

  • dagre 需要显式提供节点尺寸;不提供则使用默认值(172×36)。
  • 坐标换算不可省略,否则节点位置会偏移半个节点尺寸。
  • React Flow 状态更新必须使用新的数组引用,否则视图不刷新。
  • 关于大图性能,文档提到可在 Web Worker 中布局、对布局调用防抖、使用 useMemo、仅重新布局变化的部分,但未提供完整实现代码。
  • 文档未提及该方案的收费、价格、许可与支持的平台信息,暂未提供。
  • 更完整的 dagre 配置项参见其 reference.md,资料中未展开。