Local MCP Server
在Termux中运行本地MCP服务器,支持Ollama模型的文件读取与命令执行。
下载 11
提供使用 React Flow 构建节点式 UI 的架构建议与最佳实践。
openclaw skills install @anderskev/react-flow-architecture命令、参数、文件名以原文为准
在锁定技术栈或进入开发冲刺前执行以下序列。仅对临时原型可跳过。
onNodesChange、onConnect 等)。onlyRenderVisibleElements 时,应采纳该策略)。@xyflow/system (原生 TypeScript)
├── 核心算法(边路径、边界框、视口计算)
├── xypanzoom (基于 D3 的缩放/平移)
├── xydrag, xyhandle, xyminimap, xyresizer
└── 共享类型定义
@xyflow/react (依赖 @xyflow/system)
├── React 组件与 Hook
├── Zustand 状态管理仓库
└── 框架特定集成
@xyflow/svelte (依赖 @xyflow/system)
└── Svelte 组件与仓库含义:核心逻辑与框架无关。在贡献或排查问题时,需判断问题是出在 @xyflow/system 还是特定框架包中。
// 用于原型开发
const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);
const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);优点:简单,样板代码少
缺点:状态局限于组件树内
// Zustand 示例
import { create } from 'zustand';
interface FlowStore {
nodes: Node[];
edges: Edge[];
setNodes: (nodes: Node[]) => void;
onNodesChange: OnNodesChange;
}
const useFlowStore = create<FlowStore>((set, get) => ({
nodes: initialNodes,
edges: initialEdges,
setNodes: (nodes) => set({ nodes }),
onNodesChange: (changes) => {
set({ nodes: applyNodeChanges(changes, get().nodes) });
},
}));
// 在组件中使用
function Flow() {
const { nodes, edges, onNodesChange } = useFlowStore();
return <ReactFlow nodes={nodes} onNodesChange={onNodesChange} />;
}优点:状态全局可访问,便于持久化与同步
缺点:配置更复杂,需注意选择器优化
// 通过选择器连接
const nodes = useSelector(selectNodes);
const dispatch = useDispatch();
const onNodesChange = useCallback((changes: NodeChange[]) => {
dispatch(nodesChanged(changes));
}, [dispatch]);用户输入 → 变更事件 → Reducer/处理器 → 状态更新 → 重新渲染
↓
[拖动节点] → onNodesChange → applyNodeChanges → setNodes → ReactFlow
↓
[连接节点] → onConnect → addEdge → setEdges → ReactFlow
↓
[删除节点] → onNodesDelete → deleteElements → setNodes/setEdges → ReactFlow// 父节点包含子节点
const nodes = [
{
id: 'group-1',
type: 'group',
position: { x: 0, y: 0 },
style: { width: 300, height: 200 },
},
{
id: 'child-1',
parentId: 'group-1', // 关键:父节点引用
extent: 'parent', // 关键:限制在父节点范围内
position: { x: 10, y: 30 }, // 相对于父节点的位置
data: { label: '子节点' },
},
];注意事项:
extent: 'parent' 实现拖拽范围限制expandParent: true 可自动扩展父节点// 保存视口状态
const { toObject, setViewport } = useReactFlow();
const handleSave = () => {
const flow = toObject();
// flow.nodes, flow.edges, flow.viewport
localStorage.setItem('flow', JSON.stringify(flow));
};
const handleRestore = () => {
const flow = JSON.parse(localStorage.getItem('flow'));
setNodes(flow.nodes);
setEdges(flow.edges);
setViewport(flow.viewport);
};// 从 API 加载数据
useEffect(() => {
fetch('/api/flow')
.then(r => r.json())
.then(({ nodes, edges }) => {
setNodes(nodes);
setEdges(edges);
});
}, []);
// 延迟自动保存
const debouncedSave = useMemo(
() => debounce((nodes, edges) => {
fetch('/api/flow', {
method: 'POST',
body: JSON.stringify({ nodes, edges }),
});
}, 1000),
[]
);
useEffect(() => {
debouncedSave(nodes, edges);
}, [nodes, edges]);import dagre from 'dagre';
function getLayoutedElements(nodes: Node[], edges: Edge[]) {
const g = new dagre.graphlib.Graph();
g.setGraph({ rankdir: 'TB' });
g.setDefaultEdgeLabel(() => ({}));
nodes.forEach((node) => {
g.setNode(node.id, { width: 150, height: 50 });
});
edges.forEach((edge) => {
g.setEdge(edge.source, edge.target);
});
dagre.layout(g);
return {
nodes: nodes.map((node) => {
const pos = g.node(node.id);
return { ...node, position: { x: pos.x, y: pos.y } };
}),
edges,
};
}| 节点数量 | 建议策略 |
|---|---|
| 少于 100 个 | 使用默认设置 |
| 100 至 500 个 | 启用 onlyRenderVisibleElements |
| 500 至 1000 个 | 简化自定义节点,减少 DOM 元素数量 |
| 超过 1000 个 | 考虑虚拟化或 WebGL 替代方案 |
<ReactFlow
// 仅渲染视口内的节点和边
onlyRenderVisibleElements={true}
// 减少节点圆角(提升相交计算性能)
nodeExtent={[[-1000, -1000], [1000, 1000]]}
// 禁用不需要的功能
elementsSelectable={false}
panOnDrag={false}
zoomOnScroll={false}
/>| 受控模式 | 非受控模式 |
|---|---|
| 代码量较多 | 代码更少 |
| 完全控制状态 | 内部管理状态 |
| 易于持久化 | 需调用 toObject() |
| 适合复杂应用 | 适合原型开发 |
| 严格模式(默认) | 松散模式 |
|---|---|
| 仅支持源 → 目标连接 | 任意连接点 → 任意连接点 |
| 行为可预测 | 更灵活 |
| 适用于数据流场景 | 适用于图表类场景 |
<ReactFlow connectionMode={ConnectionMode.Loose} />| 默认边 | 自定义边 |
|---|---|
| 渲染速度快 | 更多控制权 |
| 样式有限 | 支持任意 SVG 或 HTML |
| 适用于简单场景 | 适用于复杂标签需求 |
已收录 5 个 Skill