1. 对比
React Flow 更侧重于在 React 生态系统中创建交互式节点图,而 X6 是一个更通用的图编辑引擎,支持多种前端框架,功能更为强大和灵活。如果你正在开发一个基于 React 的项目,并且需要快速实现交互式节点图,如流程图、工作流图等,React Flow 是一个很好的选择。
- 轻量级,核心功能聚焦于流程图、工作流设计。
- 提供基础节点/连线拖拽、连接点(Handles)、撤销重做、子流程图等。
- 插件扩展能力较弱,适合中小复杂度场景。
- 示例简洁,上手成本低。
- 功能全面,支持流程图、ER图、拓扑图、BPMN等复杂场景。
- 高级特性:自定义节点/连线规则、动画、键盘快捷键、网格布局、对齐线、历史记录管理等。
- 内置 Stencil(侧边栏组件库)、Clipboard(跨画布复制粘贴)、Transform(画布变换)等工具。
- 支持 JSON 序列化和 GraphML 格式导入导出。
2. React Flow流程图使用
基本操作
import {
addEdge,
Edge,
Node,
ReactFlow,
useEdgesState,
useNodesState,
} from '@xyflow/react';
import { useCallback } from 'react';
const initialNodes = [
{ id: '1', position: { x: 0, y: 0 }, data: { label: 'Node 1' } },
{ id: '2', position: { x: 200, y: 0 }, data: { label: 'Node 2' } },
]; // 初始Nodes数据定义
const initialEdges = [{ id: 'e1-2', source: '1', target: '2' }]; // 初始Edges数据定义
function Flow0() {
// 使用ReactFlow内部hooks创建数据以及数据流转处理方法
const [nodes, setNodes, onNodesChange] = useNodesState<Node>(initialNodes);
const [edges, setEdges, onEdgesChange] = useEdgesState<Edge>(initialEdges);
const onConnect = useCallback(
(params: any) => setEdges(eds => addEdge(params, eds)),
[setEdges],
);
return (
<div style={{ width: '100vw', height: '100vh' }}>
<ReactFlow
maxZoom={1.1}
fitView
nodes={nodes}
edges={edges}
onConnect={onConnect}
onNodesChange={onNodesChange}
onEdgesChange={onEdgesChange}
/>
</div>
);
}
export default Flow0;
通过上面的代码,可以创建出一个含有两个节点、一条连线的基础流程图,同时具备基础交互:节点拖拽,节点之间建立连线,退格键删除连线等。
⚠️注意事项:
- 必须导入 React Flow 样式表。
<ReactFlow />组件必须封装在具有宽度和高度的元素中。
受控模式
使用官方提供的 useNodesState、useEdgesState 方法,获得的返回值中含有完整的“响应事件 -> 处理当前数据 -> 返回新状态”的流程。
const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);
const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);
在 onNodeChange 中会接收到混合了多种类型的变更的数组,如节点删除、节点选中、节点位置变更、节点尺寸变化等,onEdgesChange 同理。
The onNodesChange callback takes an array of NodeChange objects that you should use to update your flow's state. The NodeChange type is a union of six different object types that represent that various ways an node can change in a flow.
export type NodeChange<NodeType extends NodeBase = NodeBase> = NodeDimensionChange | NodePositionChange | NodeSelectionChange | NodeRemoveChange | NodeAddChange<NodeType> | NodeReplaceChange<NodeType>;
export type NodeRemoveChange = {
id: string;
type: 'remove';
};
...
// 处理节点变更
const onNodesChange = useCallback(
(changes) => {
setNodes((prevNodes) =>
changes.reduce((acc, change) => {
switch (change.type) {
case 'remove':
return acc.filter((node) => node.id !== change.id);
...
...
default:
return acc;
}
}, prevNodes)
);
},
[]
);
同时,对于部分变更需要特殊处理时,ReactFlow还提供了applyNodeChanges 函数,可手动筛选变更类型,批量处理变更。
import { applyNodeChanges } from 'reactflow';
const [nodes, setNodes] = useState([]);
const onNodesChange = useCallback((changes) => {
setNodes((prevNodes) => {
// 先处理自定义变更
const customChanges = changes.filter(change => change.type === 'add');
const customUpdatedNodes = customUpdateChanges(customChanges, prevNodes)
// 再用 applyNodeChanges 处理剩余变更
const standardChanges = changes.filter(change => change.type !== 'add');
return applyNodeChanges(standardChanges, customUpdatedNodes);
});
}, []);
自定义节点/边
如果默认UI样式或者节点label不满足我们的需求,如下:
可以通过Node节点提供的
type以及data字段,自定义节点渲染。
export type NodeBase<NodeData,NodeType> = {
id: string
data: NodeData
type?: NodeType
...
}
function RoundedCornersNode({data}: {data: {num: number}}) {
return (
<div
style={{
background: '#d9e9fa',
borderRadius: '4px',
minWidth: 100,
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
height: 40,
}}
>
// Handle 用于渲染节点间添加连线的把柄
<Handle type="target" position={Position.Top} />
{data.num}
<Handle type="source" position={Position.Bottom} />
</div>
);
}
const nodeTypes = {
rounded: RoundedCornersNode,
};
<ReactFlow nodeTypes={nodeTypes} .../>
下一篇会写如何实现拖放新增节点