Reflex Enterprise React Flow 核心概念全解:节点、连接线与视口机制
【免费下载链接】reflex🕸️ Web apps in pure Python 🐍项目地址: https://gitcode.com/GitHub_Trending/re/reflex
节点(Node)、句柄(Handle)、边(Edge)、连接线(Connection Line)与视口(Viewport)构成了 Reflex Enterprise 中rxe.flow交互式流程图的五大核心概念。本文以 docs/enterprise/react_flow/overview.md 为主线,结合该目录下的 nodes.md、edges.md、components.md 等系列文档,系统讲解这些基础构件的数据结构、配置方式与交互原理,帮助你从零理解并搭建一个可拖拽、可连接、可缩放的流程图应用。
从整体理解一个 Flow 的组成
在 Reflex Enterprise 中,一个流程图(Flow)本质上是由节点(Nodes)和连接节点的边(Edges)组成的交互式图(interactive graph)。所有视觉元素最终都渲染在 React Flow 的画布之上,而 Reflex 通过reflex_enterprise(简称rxe)下的rxe.flow组件树把这一整套能力封装成纯 Python 的组件体系。
理解 Flow 需要抓住以下五个基本构件:
| 构件 | 作用 |
|---|---|
| 节点(Node) | 流程图的构建单元,承载内容与功能 |
| 句柄(Handle) | 节点上供边连接的锚点 |
| 边(Edge) | 连接两个节点的连线 |
| 连接线(Connection Line) | 拖拽新建连接时出现的占位边 |
| 视口(Viewport) | 包含整个流程的可视区域,负责缩放与平移 |
下文逐一展开每个概念,并给出可直接复用的配置示例。
Nodes:流程图的构建单元
节点是流程的积木。除了少数内置节点类型,真正的能力来自自定义节点:你可以让节点包含交互元素、展示动态数据、支持多个连接点。框架提供基础,样式与功能由你定义。
节点的数据形态:一个 Python 字典
在 Reflex Enterprise 中,节点被表示为带有固定字段的 Python 字典,核心字段如下:
id(str)—— 节点的唯一标识符;position(dict)—— 节点的位置,含x、y坐标;data(dict)—— 传递给节点组件的任意数据;type(str)—— 在node_types中定义的节点类型;sourcePosition/targetPosition(str)—— 控制源句柄/目标句柄的位置("top"、"right"、"bottom"、"left");hidden、selected、draggable、selectable、connectable、deletable(bool)—— 控制节点的可见性、选中状态与各项交互能力;width、height(float)—— 节点尺寸;parentId(str)—— 父节点 ID,用于创建子流程(sub-flows);style(dict)—— 节点自定义样式;className(str)—— 节点的 CSS 类名。
三种内置节点类型
Reflex Flow 提供了三种开箱即用的内置节点类型(详见 nodes.md):
nodes: list[Node] = [ { "id": "1", "type": "input", "position": {"x": 100, "y": 100}, "data": {"label": "Start"}, }, { "id": "2", "type": "default", "position": {"x": 300, "y": 100}, "data": {"label": "Process"}, }, { "id": "3", "type": "output", "position": {"x": 500, "y": 100}, "data": {"label": "End"}, }, ]- input—— 流程入口点,只带源句柄(source handles);
- default—— 标准节点,同时具备源句柄与目标句柄;
- output—— 流程出口点,只带目标句柄(target handles)。
节点定位、样式与句柄配置
定位、样式与句柄都是节点上可以独立配置的属性:
# 节点定位:通过 position 的 x/y 决定节点在画布上的坐标 node = { "id": "positioned-node", "type": "default", "position": {"x": 250, "y": 150}, "data": {"label": "Positioned Node"}, } # 节点样式:通过 style 直接传入 CSS 属性 styled_node = { "id": "styled-node", "type": "default", "position": {"x": 100, "y": 200}, "data": {"label": "Custom Style"}, "style": { "background": "#ff6b6b", "color": "white", "border": "2px solid #ff5252", "borderRadius": "8px", "padding": "10px", }, } # 句柄位置:sourcePosition / targetPosition 决定边的出入方向 node_with_handles = { "id": "handle-node", "type": "default", "position": {"x": 300, "y": 300}, "data": {"label": "Custom Handles"}, "sourcePosition": "right", "targetPosition": "left", }Handles:边与节点的连接锚点
句柄是节点上边所依附的点。它们通常出现在节点的上、下、左、右侧,但可以自由定位与定制样式。一个节点可以拥有多个句柄,从而支持复杂的连接布局。
在自定义节点中,句柄通过rxe.flow.handle组件显式声明。以 nodes.md 中的"颜色选择器"自定义节点为例:
import reflex as rx import reflex_enterprise as rxe @rx.memo def color_selector_node( data: rx.Var[dict], isConnectable: rx.Var[bool] ) -> rx.Component: data = data.to(dict) return rx.el.div( rxe.flow.handle( type="target", position="left", is_connectable=isConnectable, ), rx.el.div( "Custom Color Picker Node: ", rx.el.strong(data["color"]), ), rx.el.input( class_name="nodrag", type="color", on_change=CustomNodeState.on_change_color, default_value=data["color"], ), rxe.flow.handle( type="source", position="right", is_connectable=isConnectable, ), )该示例展示了句柄的两个关键属性:
type——"target"(接收连接)或"source"(发出连接);position—— 句柄在节点上的方位;is_connectable—— 是否允许建立连接,可结合业务逻辑动态控制(例如连接数上限,见 examples.md 中的 Connection Limit 示例)。
另外注意class_name="nodrag":给节点内部的可交互元素加上nodrag类,可以避免拖拽节点时误触发输入控件。
自定义节点的挂载方式
自定义节点本质上是标准的 Reflex 组件,通过node_types字典映射到type字符串上:
rxe.flow( ..., node_types={"selectorNode": color_selector_node}, )因为是标准组件,你可以在节点内渲染任意内容、实现任意功能,并通过data字段把动态数据注入节点。
Edges:节点之间的连接
边是连接两个节点的连线。每条边都需要一个源节点(source)与一个目标节点(target)。边可以被定制样式,多句柄节点也能挂载多条边;自定义边可以包含交互元素、专门的路径算法或超越简单线条的独特视觉样式。
边的数据形态
边同样以 Python 字典表示(详见 edges.md):
id(str)—— 边的唯一标识符;source/target(str)—— 源节点与目标节点的 ID;type(str)—— 在edge_types中定义的边类型;sourceHandle/targetHandle(str | None)—— 可选的源/目标句柄 ID;animated(bool)—— 是否启用动画;hidden、deletable、selectable(bool)—— 可见性与交互开关;data(dict)—— 任意元数据;label(Any)—— 沿边渲染的标签文本;style(dict)、className(str)—— 边的样式控制。
五种内置边类型
edges: list[Edge] = [ {"id": "e1", "source": "1", "target": "2", "type": "default"}, {"id": "e2", "source": "2", "target": "3", "type": "straight"}, {"id": "e3", "source": "3", "target": "4", "type": "step"}, {"id": "e4", "source": "4", "target": "5", "type": "smoothstep"}, {"id": "e5", "source": "5", "target": "6", "type": "bezier"}, ]- default—— 标准曲线边;
- straight—— 节点间的直线;
- step—— 直角折线路径;
- smoothstep—— 平滑的直角折线路径;
- bezier—— 贝塞尔曲线路径。
边样式、动画与标签
# 样式:通过 style 控制描边颜色与粗细 edges: list[Edge] = [ { "id": "styled-edge", "source": "1", "target": "2", "style": { "stroke": "#ff6b6b", "strokeWidth": 3, }, } ] # 动画:animated=True 让流动的点沿边运动 edges: list[Edge] = [ { "id": "animated-edge", "source": "1", "target": "2", "animated": True, "style": {"stroke": "#4dabf7"}, } ] # 标签:label 在边的中间位置渲染文本 edges: list[Edge] = [ { "id": "labeled-edge", "source": "1", "target": "2", "label": "Connection", "style": {"stroke": "#51cf66"}, } ]自定义边
当默认直线或贝塞尔连接器无法满足需求时,可以通过edge_types定义自定义边类型。例如在 edges.md 的"带删除按钮的边"示例中,自定义边组合了rxe.flow.base_edge(渲染路径)与rxe.flow.edge_label_renderer(渲染中点处的交互按钮):
@rx.memo def button_edge( id: rx.Var[str], sourceX: rx.Var[float], sourceY: rx.Var[float], targetX: rx.Var[float], targetY: rx.Var[float], sourcePosition: rx.Var[Position], targetPosition: rx.Var[Position], markerEnd: rx.Var[str], ) -> rx.Fragment: bezier_path = rxe.components.flow.util.get_bezier_path( source_x=sourceX, source_y=sourceY, target_x=targetX, target_y=targetY, source_position=sourcePosition, target_position=targetPosition, ) return rx.fragment( rxe.flow.base_edge(path=bezier_path.path, markerEnd=markerEnd), rxe.flow.edge_label_renderer( rx.el.div( rx.el.button( "×", on_click=rx.run_script(...), # 通过 api.set_edges 删除当前边 style={"position": "absolute", "left": f"{bezier_path.label_x}px", ...}, ), ) ), )其中get_bezier_path返回的label_x/label_y用于把按钮精确定位到边的中点。自定义边通过edge_types={"button": button_edge}挂载到 Flow 上。
Connection Line:拖拽连接时的占位边
当用户从一个句柄点击并拖向另一个句柄来创建新边时,拖拽过程中出现的占位边被称为连接线(Connection Line)。连接线的行为与普通边一致,其外观与行为同样可以被定制。
连接线的生命周期由 Flow 的事件驱动:
on_connect—— 用户完成一次有效连接(从一个句柄拖到另一个句柄)时触发,配合rxe.flow.util.add_edge把新边写入状态;on_connect_end—— 连接交互结束时触发,无论是否有效。可结合rxe.flow.api.screen_to_flow_position把鼠标屏幕坐标转换为画布坐标,实现"在画布空白处松手即新建节点"等高级交互(见 examples.md 的 "Add Node on Edge Drop" 示例)。
一个典型的连接接线方式如下(完整示例见 interactivity.md):
rxe.flow( ..., on_connect=lambda connection: FlowState.set_edges( rxe.flow.util.add_edge(connection, FlowState.edges) ), )Viewport:视口与画布坐标体系
视口是包含整个流程的可视区域。每个节点都有 x、y 坐标表示其位置;移动视口会改变这些坐标,放大或缩小则调整缩放级别(zoom)。视口保证了图表始终可导航、可交互。
rxe.flow提供了若干与视口相关的配置:
fit_view: bool—— 为True时,Flow 会自动缩放并平移,使所有初始节点完整适配视口(基础示例见 basic_flow.md);fit_view_options: FitViewOptions—— 定制 fitView 行为(如{"padding": 2});default_viewport—— 设置初始视口,例如{"x": 0, "y": 0, "zoom": 1.5};snap_to_grid/snap_grid—— 是否启用网格吸附及网格间距(如snap_grid=(20, 20))。
视口相关的程序化能力由rxe.flow.api提供(详见 hooks.md):
screen_to_flow_position(x, y, snap_to_grid=False)—— 把屏幕像素坐标转换为 Flow 画布坐标;flow_to_screen_position(x, y)—— 把画布内坐标转换为屏幕像素坐标。
例如把用户松开连接的鼠标位置转换为画布坐标:
rxe.flow( ..., on_connect_end=lambda connection_status, event: FlowState.handle_connect_end( connection_status, rxe.flow.api.screen_to_flow_position( x=event.client_x, y=event.client_y, ), ), )让五大概念协作:一个完整的交互式 Flow
把上述概念串起来,就是一个完整的交互式流程图。以下代码取自 basic_flow.md,包含输入/默认/输出三种节点、一条动画边与一条普通边,并挂载了控件、背景网格与迷你地图:
import reflex as rx import reflex_enterprise as rxe from reflex_enterprise.components.flow.types import Node, Edge # 所有节点共用的样式 node_style = { "backgroundColor": "#ffcc00", "color": "#000000", "padding": "10px", "borderRadius": "5px", } class FlowState(rx.State): nodes: list[Node] = [ {"id": "1", "type": "input", "position": {"x": 100, "y": 100}, "data": {"label": "Input Node"}, "style": node_style}, {"id": "2", "type": "default", "position": {"x": 300, "y": 200}, "data": {"label": "Default Node"}, "style": node_style}, {"id": "3", "type": "output", "position": {"x": 500, "y": 100}, "data": {"label": "Output Node"}, "style": node_style}, ] edges: list[Edge] = [ {"id": "e1-2", "source": "1", "target": "2", "animated": True}, {"id": "e2-3", "source": "2", "target": "3"}, ] def flow_example(): return rx.box( rxe.flow( rxe.flow.controls(), rxe.flow.background(), rxe.flow.mini_map(), default_nodes=FlowState.nodes, default_edges=FlowState.edges, nodes=FlowState.nodes, edges=FlowState.edges, fit_view=True, attribution_position="bottom-right", ), height="100vh", width="100vw", )要点:
- 节点可移动、边会随节点动态更新,用户可从句柄拖出新连接;
- 缩放、平移与迷你地图开箱即用;
- Flow 自动适配视口(
fit_view=True),并支持浅色/深色模式; - 务必给外层容器设置显式
height与width——Flow 填满父容器,没有尺寸则不可见。
状态驱动的交互:controlled 与 uncontrolled
在 interactivity.md 中,Reflex Flow 明确区分了两种工作模式:
- 受控(controlled):传入
nodes/edges,你的 State 是唯一数据源,画布渲染状态中的确切内容。此时必须接线on_nodes_change/on_edges_change,否则拖拽、选择、删除等交互结果会被丢弃; - 非受控(uncontrolled):只传
default_nodes/default_edges,组件内部自行管理变更,但你的 State 不会感知这些变化。
受控模式的完整事件接线:
def interactive_flow(): return rx.box( rxe.flow( rxe.flow.controls(), rxe.flow.background(), rxe.flow.mini_map(), nodes=FlowState.nodes, edges=FlowState.edges, on_nodes_change=lambda node_changes: FlowState.set_nodes( rxe.flow.util.apply_node_changes(FlowState.nodes, node_changes) ), on_edges_change=lambda edge_changes: FlowState.set_edges( rxe.flow.util.apply_edge_changes(FlowState.edges, edge_changes) ), on_connect=lambda connection: FlowState.set_edges( rxe.flow.util.add_edge(connection, FlowState.edges) ), fit_view=True, attribution_position="bottom-right", ), height="100vh", width="100vw", )on_nodes_change—— 节点被拖拽、选择、缩放或删除时触发;on_edges_change—— 边被选择或删除时触发;on_connect—— 用户完成一次句柄连接时触发,rxe.flow.util.add_edge根据连接信息构建新边列表。
重要约束:
apply_node_changes/apply_edge_changes这类工具函数必须在组件代码(即接线到on_nodes_change/on_edges_change的 lambda)中使用,在客户端求值并产出更新后的列表,再交给普通的 setter 事件处理器;绝不能在@rx.event处理器内部调用它们——State 一侧只负责接收并保存结果列表。
常用组件与主题定制
四大辅助组件
rxe.flow组件体系(详见 components.md)还包含四个高频辅助组件:
| 组件 | 作用 | 关键 Props |
|---|---|---|
rxe.flow.provider | 上下文 Provider,让 Flow 的内部状态可在<ReactFlow />之外访问(多数 API hooks 依赖它) | initial_nodes、default_edges、initial_width、initial_height、fit_view、initial_fit_view_options、initial_min_zoom、initial_max_zoom、node_origin、node_extent |
rxe.flow.background | 渲染背景图案(线条、圆点或十字) | color、bg_color、variant="lines\|dots\|cross"、gap、size |
rxe.flow.controls | 缩放/适配视图/锁定视口的控制面板 | show_zoom、show_fit_view、show_interactive、position |
rxe.flow.mini_map | 流程总览小地图 | node_color、node_stroke_color、pannable、zoomable、position |
注意:rxe.flow.provider不接受任何事件处理器,所有事件(on_nodes_change、on_edges_change、on_connect等)必须设置在内部的rxe.flow上。典型用法:
rxe.flow.provider( rxe.flow( rxe.flow.background(), nodes=FlowState.nodes, edges=FlowState.edges, on_nodes_change=lambda changes: FlowState.set_nodes( rxe.flow.util.apply_node_changes(FlowState.nodes, changes) ), fit_view=True, ) )Provider 的initial_width/initial_height是服务端使用fit_view所必需的尺寸参数;node_extent用来限定节点可移动的边界。
API Hooks 一览
rxe.flow.api模块封装了 React Flow 的useReactFlowhook(须配合rxe.flow.provider使用,详见 hooks.md):
- 节点:
get_nodes()、set_nodes(nodes)、add_nodes(nodes)、get_node(id)、update_node(id, node_update, replace=False)、update_node_data(id, data_update, replace=False); - 边:
get_edges()、set_edges(edges)、add_edges(edges)、get_edge(id)、update_edge(id, edge_update, replace=False)、update_edge_data(id, data_update, replace=False); - 视口:
screen_to_flow_position/flow_to_screen_position(见上文); - 其他:
to_object()(导出 Flow 状态为 JSON)、get_intersecting_nodes(node, partially=True, nodes=None)(碰撞检测)、get_node_connections(id, handle_type, handle_id)(查询节点连接)、get_connection()(获取进行中的连接状态)。
主题定制
Flow 组件自带默认主题,可通过 CSS 变量覆盖(详见 theming.md):
.react-flow { --xy-background-color: #f7f9fb; --xy-node-border-default: 1px solid #ededed; --xy-node-boxshadow-default: 0px 3.54px 4.55px 0px #00000005; --xy-node-border-radius-default: 8px; --xy-handle-background-color-default: #ffffff; --xy-handle-border-color-default: #aaaaaa; --xy-edge-label-color-default: #505050; }通过rxe.App或rx.App的stylesheets参数引入自定义样式表:
app = rxe.App( stylesheets=[ "/css/my-custom-styles.css", ], )然后在项目assets/css/my-custom-styles.css中编写覆盖样式。除此之外,单个节点与边也可以直接用style字典或className类名做精细化定制。
小结
把握住"节点承载内容、句柄决定连接锚点、边表达关系、连接线呈现拖拽过程、视口管理导航"这五条主线,就能顺畅地阅读 nodes.md 与 edges.md 中完整的数据结构字段表,并在此基础上阅读 components.md、hooks.md 与 utils.md 理解组件、API 与工具函数,最终参考 interactivity.md 与 examples.md 构建出真正可用的交互式流程图应用。
【免费下载链接】reflex🕸️ Web apps in pure Python 🐍项目地址: https://gitcode.com/GitHub_Trending/re/reflex
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考