svelte-flow
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseSvelte Flow
Svelte Flow
Expert guide for building node-based UIs with Svelte Flow (@xyflow/svelte).
基于Svelte Flow (@xyflow/svelte) 构建节点式UI的专家指南。
Installation
安装
bash
npm install @xyflow/sveltebash
npm install @xyflow/svelteCore Setup
核心设置
svelte
<script lang="ts">
import { SvelteFlow, Background } from '@xyflow/svelte';
import '@xyflow/svelte/dist/style.css';
// Use $state.raw for nodes and edges (Svelte 5)
let nodes = $state.raw([
{ id: '1', data: { label: 'Node 1' }, position: { x: 0, y: 0 } },
{ id: '2', data: { label: 'Node 2' }, position: { x: 0, y: 100 } }
]);
let edges = $state.raw([
{ id: 'e1-2', source: '1', target: '2' }
]);
</script>
<SvelteFlow bind:nodes bind:edges fitView>
<Background />
</SvelteFlow>svelte
<script lang="ts">
import { SvelteFlow, Background } from '@xyflow/svelte';
import '@xyflow/svelte/dist/style.css';
// Use $state.raw for nodes and edges (Svelte 5)
let nodes = $state.raw([
{ id: '1', data: { label: 'Node 1' }, position: { x: 0, y: 0 } },
{ id: '2', data: { label: 'Node 2' }, position: { x: 0, y: 100 } }
]);
let edges = $state.raw([
{ id: 'e1-2', source: '1', target: '2' }
]);
</script>
<SvelteFlow bind:nodes bind:edges fitView>
<Background />
</SvelteFlow>Key Concepts
核心概念
Nodes
节点
Each node requires:
- : Unique identifier
id - :
position{ x: number, y: number } - : Object with custom properties (typically includes
data)label - Optional: ,
type,style,hidden,width,height,sourcePositiontargetPosition
typescript
const node = {
id: '1',
type: 'custom',
position: { x: 100, y: 100 },
data: { label: 'My Node', color: '#ff0000' },
style: 'background: #f0f0f0',
width: 150,
height: 100
};每个节点需要包含以下属性:
- :唯一标识符
id - :
position(坐标位置){ x: number, y: number } - :包含自定义属性的对象(通常包含
data标签)label - 可选属性:(类型)、
type(样式)、style(隐藏状态)、hidden(宽度)、width(高度)、height(源连接点位置)、sourcePosition(目标连接点位置)targetPosition
typescript
const node = {
id: '1',
type: 'custom',
position: { x: 100, y: 100 },
data: { label: 'My Node', color: '#ff0000' },
style: 'background: #f0f0f0',
width: 150,
height: 100
};Edges
边
Each edge requires:
- : Unique identifier
id - : Source node ID
source - : Target node ID
target - Optional: ,
type,label,animated,markerEnd,markerStart,stylehidden
typescript
const edge = {
id: 'e1-2',
source: '1',
target: '2',
type: 'smoothstep',
label: 'connects to',
animated: true,
markerEnd: { type: MarkerType.ArrowClosed }
};每条边需要包含以下属性:
- :唯一标识符
id - :源节点ID
source - :目标节点ID
target - 可选属性:(类型)、
type(标签)、label(动画效果)、animated(末端标记)、markerEnd(起始标记)、markerStart(样式)、style(隐藏状态)hidden
typescript
const edge = {
id: 'e1-2',
source: '1',
target: '2',
type: 'smoothstep',
label: 'connects to',
animated: true,
markerEnd: { type: MarkerType.ArrowClosed }
};Built-in Types
内置类型
Edge Types:
- : Straight line
default - : Smooth 90-degree turns
smoothstep - : Hard 90-degree turns
step - : Alias for default
straight - : Curved bezier line
bezier
Node Types:
- : Basic rectangle node
default - : Node with only source handles
input - : Node with only target handles
output
边类型:
- :直线
default - :平滑90度转折
smoothstep - :直角90度转折
step - :
straight的别名default - :贝塞尔曲线
bezier
节点类型:
- :基础矩形节点
default - :仅包含源连接点的节点
input - :仅包含目标连接点的节点
output
Custom Nodes
自定义节点
svelte
<!-- CustomNode.svelte -->
<script lang="ts" module>
import type { Node, NodeProps } from '@xyflow/svelte';
export type CustomNodeType = Node<{
label: string;
color?: string;
}>;
</script>
<script lang="ts">
import { Handle, Position } from '@xyflow/svelte';
let { data }: NodeProps<CustomNodeType> = $props();
</script>
<div class="custom-node" style:background={data.color}>
<Handle type="target" position={Position.Left} />
<div>{data.label}</div>
<Handle type="source" position={Position.Right} />
</div>
<style>
.custom-node {
padding: 10px;
border-radius: 5px;
border: 1px solid #ddd;
}
</style>Register custom nodes:
svelte
<script lang="ts">
import CustomNode from './CustomNode.svelte';
const nodeTypes = {
custom: CustomNode
};
let nodes = $state.raw([
{ id: '1', type: 'custom', data: { label: 'Custom', color: '#ff7000' }, position: { x: 0, y: 0 } }
]);
</script>
<SvelteFlow bind:nodes {nodeTypes} bind:edges fitView />svelte
<!-- CustomNode.svelte -->
<script lang="ts" module>
import type { Node, NodeProps } from '@xyflow/svelte';
export type CustomNodeType = Node<{
label: string;
color?: string;
}>;
</script>
<script lang="ts">
import { Handle, Position } from '@xyflow/svelte';
let { data }: NodeProps<CustomNodeType> = $props();
</script>
<div class="custom-node" style:background={data.color}>
<Handle type="target" position={Position.Left} />
<div>{data.label}</div>
<Handle type="source" position={Position.Right} />
</div>
<style>
.custom-node {
padding: 10px;
border-radius: 5px;
border: 1px solid #ddd;
}
</style>注册自定义节点:
svelte
<script lang="ts">
import CustomNode from './CustomNode.svelte';
const nodeTypes = {
custom: CustomNode
};
let nodes = $state.raw([
{ id: '1', type: 'custom', data: { label: 'Custom', color: '#ff7000' }, position: { x: 0, y: 0 } }
]);
</script>
<SvelteFlow bind:nodes {nodeTypes} bind:edges fitView />Custom Edges
自定义边
svelte
<!-- CustomEdge.svelte -->
<script lang="ts">
import { BaseEdge, EdgeLabel, getStraightPath, type EdgeProps } from '@xyflow/svelte';
let { id, sourceX, sourceY, targetX, targetY, label }: EdgeProps = $props();
let [edgePath, labelX, labelY] = $derived(
getStraightPath({ sourceX, sourceY, targetX, targetY })
);
</script>
<BaseEdge {id} path={edgePath} />
{#if label}
<EdgeLabel x={labelX} y={labelY}>
<div class="edge-label">{label}</div>
</EdgeLabel>
{/if}Path helpers available:
getStraightPath()getBezierPath()getSmoothStepPath()
Register custom edges:
svelte
<script lang="ts">
const edgeTypes = {
custom: CustomEdge
};
</script>
<SvelteFlow bind:nodes bind:edges {edgeTypes} fitView />svelte
<!-- CustomEdge.svelte -->
<script lang="ts">
import { BaseEdge, EdgeLabel, getStraightPath, type EdgeProps } from '@xyflow/svelte';
let { id, sourceX, sourceY, targetX, targetY, label }: EdgeProps = $props();
let [edgePath, labelX, labelY] = $derived(
getStraightPath({ sourceX, sourceY, targetX, targetY })
);
</script>
<BaseEdge {id} path={edgePath} />
{#if label}
<EdgeLabel x={labelX} y={labelY}>
<div class="edge-label">{label}</div>
</EdgeLabel>
{/if}可用路径辅助函数:
getStraightPath()getBezierPath()getSmoothStepPath()
注册自定义边:
svelte
<script lang="ts">
const edgeTypes = {
custom: CustomEdge
};
</script>
<SvelteFlow bind:nodes bind:edges {edgeTypes} fitView />Updating State
更新状态
Critical: Nodes and edges are immutable. Create new objects to trigger updates.
svelte
<script lang="ts">
// ❌ Won't work - direct mutation
nodes[0].position.x = 100;
// ✅ Works - create new array
nodes = nodes.map((node) => {
if (node.id === '1') {
return { ...node, position: { ...node.position, x: 100 } };
}
return node;
});
// ✅ Also works - using helper from useSvelteFlow
import { useSvelteFlow } from '@xyflow/svelte';
const { updateNode } = useSvelteFlow();
updateNode('1', (node) => ({
...node,
position: { ...node.position, x: 100 }
}));
</script>重要提示: 节点和边是不可变的,需要创建新对象来触发更新。
svelte
<script lang="ts">
// ❌ 无效 - 直接修改
nodes[0].position.x = 100;
// ✅ 有效 - 创建新数组
nodes = nodes.map((node) => {
if (node.id === '1') {
return { ...node, position: { ...node.position, x: 100 } };
}
return node;
});
// ✅ 同样有效 - 使用useSvelteFlow提供的辅助函数
import { useSvelteFlow } from '@xyflow/svelte';
const { updateNode } = useSvelteFlow();
updateNode('1', (node) => ({
...node,
position: { ...node.position, x: 100 }
}));
</script>Event Handling
事件处理
Common event handlers:
svelte
<SvelteFlow
bind:nodes
bind:edges
onnodeclick={(event) => console.log('node clicked', event.targetNode)}
onnodedrag={(event) => console.log('node dragging', event.targetNode)}
onnodedragstop={(event) => console.log('drag ended', event.targetNode)}
onedgeclick={(event) => console.log('edge clicked', event.edge)}
onconnect={(params) => {
edges = [...edges, { id: `e${params.source}-${params.target}`, ...params }];
}}
/>Available events:
- ,
onnodeclickonnodedoubleclick - ,
onnodedrag,onnodedragstartonnodedragstop - ,
onedgeclickonedgedoubleclick - ,
onconnect,onconnectstartonconnectend - (async, can prevent deletion)
onbeforedelete - ,
oninit,onmove,onmovestartonmoveend
常用事件处理器:
svelte
<SvelteFlow
bind:nodes
bind:edges
onnodeclick={(event) => console.log('node clicked', event.targetNode)}
onnodedrag={(event) => console.log('node dragging', event.targetNode)}
onnodedragstop={(event) => console.log('drag ended', event.targetNode)}
onedgeclick={(event) => console.log('edge clicked', event.edge)}
onconnect={(params) => {
edges = [...edges, { id: `e${params.source}-${params.target}`, ...params }];
}}
/>可用事件:
- (节点点击)、
onnodeclick(节点双击)onnodedoubleclick - (节点拖动中)、
onnodedrag(节点开始拖动)、onnodedragstart(节点停止拖动)onnodedragstop - (边点击)、
onedgeclick(边双击)onedgedoubleclick - (连接创建)、
onconnect(开始连接)、onconnectstart(结束连接)onconnectend - (删除前触发,异步,可阻止删除)
onbeforedelete - (初始化完成)、
oninit(画布移动中)、onmove(画布开始移动)、onmovestart(画布停止移动)onmoveend
Layout Algorithms
布局算法
Dagre (Hierarchical)
Dagre(层级布局)
typescript
import dagre from '@dagrejs/dagre';
import { Position } from '@xyflow/svelte';
function getLayoutedElements(nodes: Node[], edges: Edge[], direction = 'TB') {
const dagreGraph = new dagre.graphlib.Graph();
dagreGraph.setDefaultEdgeLabel(() => ({}));
const isHorizontal = direction === 'LR';
dagreGraph.setGraph({ rankdir: direction });
const nodeWidth = 172;
const nodeHeight = 36;
nodes.forEach((node) => {
dagreGraph.setNode(node.id, { width: nodeWidth, height: nodeHeight });
});
edges.forEach((edge) => {
dagreGraph.setEdge(edge.source, edge.target);
});
dagre.layout(dagreGraph);
const layoutedNodes = nodes.map((node) => {
const nodeWithPosition = dagreGraph.node(node.id);
return {
...node,
targetPosition: isHorizontal ? Position.Left : Position.Top,
sourcePosition: isHorizontal ? Position.Right : Position.Bottom,
position: {
x: nodeWithPosition.x - nodeWidth / 2,
y: nodeWithPosition.y - nodeHeight / 2,
},
};
});
return { nodes: layoutedNodes, edges };
}typescript
import dagre from '@dagrejs/dagre';
import { Position } from '@xyflow/svelte';
function getLayoutedElements(nodes: Node[], edges: Edge[], direction = 'TB') {
const dagreGraph = new dagre.graphlib.Graph();
dagreGraph.setDefaultEdgeLabel(() => ({}));
const isHorizontal = direction === 'LR';
dagreGraph.setGraph({ rankdir: direction });
const nodeWidth = 172;
const nodeHeight = 36;
nodes.forEach((node) => {
dagreGraph.setNode(node.id, { width: nodeWidth, height: nodeHeight });
});
edges.forEach((edge) => {
dagreGraph.setEdge(edge.source, edge.target);
});
dagre.layout(dagreGraph);
const layoutedNodes = nodes.map((node) => {
const nodeWithPosition = dagreGraph.node(node.id);
return {
...node,
targetPosition: isHorizontal ? Position.Left : Position.Top,
sourcePosition: isHorizontal ? Position.Right : Position.Bottom,
position: {
x: nodeWithPosition.x - nodeWidth / 2,
y: nodeWithPosition.y - nodeHeight / 2,
},
};
});
return { nodes: layoutedNodes, edges };
}Advanced Features
高级功能
Floating Edges
浮动边
Edges that dynamically connect to the closest point on node boundaries:
svelte
<!-- FloatingEdge.svelte -->
<script lang="ts">
import { BaseEdge, getBezierPath, type EdgeProps } from '@xyflow/svelte';
import { getEdgeParams } from './utils';
let { id, source, target, markerEnd }: EdgeProps = $props();
const { sx, sy, tx, ty, sourcePos, targetPos } = $derived(
getEdgeParams(source, target)
);
let [edgePath] = $derived(
getBezierPath({
sourceX: sx,
sourceY: sy,
sourcePosition: sourcePos,
targetX: tx,
targetY: ty,
targetPosition: targetPos,
})
);
</script>
<BaseEdge {id} path={edgePath} {markerEnd} />Register with :
ConnectionMode.Loosesvelte
<script lang="ts">
import { ConnectionMode } from '@xyflow/svelte';
</script>
<SvelteFlow
bind:nodes
bind:edges
edgeTypes={{ floating: FloatingEdge }}
connectionMode={ConnectionMode.Loose}
fitView
/>可动态连接到节点边界最近点的边:
svelte
<!-- FloatingEdge.svelte -->
<script lang="ts">
import { BaseEdge, getBezierPath, type EdgeProps } from '@xyflow/svelte';
import { getEdgeParams } from './utils';
let { id, source, target, markerEnd }: EdgeProps = $props();
const { sx, sy, tx, ty, sourcePos, targetPos } = $derived(
getEdgeParams(source, target)
);
let [edgePath] = $derived(
getBezierPath({
sourceX: sx,
sourceY: sy,
sourcePosition: sourcePos,
targetX: tx,
targetY: ty,
targetPosition: targetPos,
})
);
</script>
<BaseEdge {id} path={edgePath} {markerEnd} />配合注册使用:
ConnectionMode.Loosesvelte
<script lang="ts">
import { ConnectionMode } from '@xyflow/svelte';
</script>
<SvelteFlow
bind:nodes
bind:edges
edgeTypes={{ floating: FloatingEdge }}
connectionMode={ConnectionMode.Loose}
fitView
/>Proximity Connect
邻近连接
Auto-connect nodes when dragged close together:
svelte
<script lang="ts">
const MIN_DISTANCE = 150;
function getClosestEdge(node: Node, nodes: Node[]) {
const closestNode = nodes.reduce(
(res, n) => {
if (n.id !== node.id) {
const dx = n.position.x - node.position.x;
const dy = n.position.y - node.position.y;
const d = Math.sqrt(dx * dx + dy * dy);
if (d < res.distance && d < MIN_DISTANCE) {
res.distance = d;
res.node = n;
}
}
return res;
},
{ distance: Number.MAX_VALUE, node: null }
);
if (!closestNode.node) return null;
const closeNodeIsSource = closestNode.node.position.x < node.position.x;
return {
id: closeNodeIsSource
? `${node.id}-${closestNode.node.id}`
: `${closestNode.node.id}-${node.id}`,
source: closeNodeIsSource ? closestNode.node.id : node.id,
target: closeNodeIsSource ? node.id : closestNode.node.id,
class: 'temp',
};
}
function onNodeDrag({ targetNode: node }) {
const closestEdge = getClosestEdge(node, nodes);
edges = edges.filter(e => e.class !== 'temp');
if (closestEdge && !edges.some(e => e.source === closestEdge.source && e.target === closestEdge.target)) {
edges = [...edges, closestEdge];
}
}
function onNodeDragStop() {
edges = edges.map((edge) =>
edge.class === 'temp' ? { ...edge, class: '' } : edge
);
}
</script>
<SvelteFlow
bind:nodes
bind:edges
onnodedrag={onNodeDrag}
onnodedragstop={onNodeDragStop}
fitView
/>当节点拖动到彼此附近时自动创建连接:
svelte
<script lang="ts">
const MIN_DISTANCE = 150;
function getClosestEdge(node: Node, nodes: Node[]) {
const closestNode = nodes.reduce(
(res, n) => {
if (n.id !== node.id) {
const dx = n.position.x - node.position.x;
const dy = n.position.y - node.position.y;
const d = Math.sqrt(dx * dx + dy * dy);
if (d < res.distance && d < MIN_DISTANCE) {
res.distance = d;
res.node = n;
}
}
return res;
},
{ distance: Number.MAX_VALUE, node: null }
);
if (!closestNode.node) return null;
const closeNodeIsSource = closestNode.node.position.x < node.position.x;
return {
id: closeNodeIsSource
? `${node.id}-${closestNode.node.id}`
: `${closestNode.node.id}-${node.id}`,
source: closeNodeIsSource ? closestNode.node.id : node.id,
target: closeNodeIsSource ? node.id : closestNode.node.id,
class: 'temp',
};
}
function onNodeDrag({ targetNode: node }) {
const closestEdge = getClosestEdge(node, nodes);
edges = edges.filter(e => e.class !== 'temp');
if (closestEdge && !edges.some(e => e.source === closestEdge.source && e.target === closestEdge.target)) {
edges = [...edges, closestEdge];
}
}
function onNodeDragStop() {
edges = edges.map((edge) =>
edge.class === 'temp' ? { ...edge, class: '' } : edge
);
}
</script>
<SvelteFlow
bind:nodes
bind:edges
onnodedrag={onNodeDrag}
onnodedragstop={onNodeDragStop}
fitView
/>Edge Reconnection
边重连
Allow users to drag edge endpoints to different nodes:
svelte
<!-- ReconnectableEdge.svelte -->
<script lang="ts">
import {
BaseEdge,
EdgeReconnectAnchor,
getBezierPath,
type EdgeProps
} from '@xyflow/svelte';
let { sourceX, sourceY, targetX, targetY, selected, ...props }: EdgeProps = $props();
const [edgePath] = $derived(
getBezierPath({ sourceX, sourceY, targetX, targetY })
);
let reconnecting = $state(false);
</script>
{#if !reconnecting}
<BaseEdge path={edgePath} {...props} />
{/if}
{#if selected}
<EdgeReconnectAnchor
bind:reconnecting
type="source"
position={{ x: sourceX, y: sourceY }}
/>
<EdgeReconnectAnchor
bind:reconnecting
type="target"
position={{ x: targetX, y: targetY }}
/>
{/if}允许用户拖动边的端点连接到其他节点:
svelte
<!-- ReconnectableEdge.svelte -->
<script lang="ts">
import {
BaseEdge,
EdgeReconnectAnchor,
getBezierPath,
type EdgeProps
} from '@xyflow/svelte';
let { sourceX, sourceY, targetX, targetY, selected, ...props }: EdgeProps = $props();
const [edgePath] = $derived(
getBezierPath({ sourceX, sourceY, targetX, targetY })
);
let reconnecting = $state(false);
</script>
{#if !reconnecting}
<BaseEdge path={edgePath} {...props} />
{/if}
{#if selected}
<EdgeReconnectAnchor
bind:reconnecting
type="source"
position={{ x: sourceX, y: sourceY }}
/>
<EdgeReconnectAnchor
bind:reconnecting
type="target"
position={{ x: targetX, y: targetY }}
/>
{/if}Delete Node and Reconnect Edges
删除节点并重连边
svelte
<script lang="ts">
import { getIncomers, getOutgoers, getConnectedEdges, type OnBeforeDelete } from '@xyflow/svelte';
const onbeforedelete: OnBeforeDelete = async ({ nodes: deletedNodes }) => {
let remainingNodes = [...nodes];
edges = deletedNodes.reduce((acc, node) => {
const incomers = getIncomers(node, remainingNodes, acc);
const outgoers = getOutgoers(node, remainingNodes, acc);
const connectedEdges = getConnectedEdges([node], acc);
const remainingEdges = acc.filter((edge) => !connectedEdges.includes(edge));
const createdEdges = incomers.flatMap(({ id: source }) =>
outgoers.map(({ id: target }) => ({
id: `${source}->${target}`,
source,
target,
}))
);
remainingNodes = remainingNodes.filter((rn) => rn.id !== node.id);
return [...remainingEdges, ...createdEdges];
}, edges);
nodes = remainingNodes;
return true;
};
</script>
<SvelteFlow bind:nodes bind:edges {onbeforedelete} fitView />svelte
<script lang="ts">
import { getIncomers, getOutgoers, getConnectedEdges, type OnBeforeDelete } from '@xyflow/svelte';
const onbeforedelete: OnBeforeDelete = async ({ nodes: deletedNodes }) => {
let remainingNodes = [...nodes];
edges = deletedNodes.reduce((acc, node) => {
const incomers = getIncomers(node, remainingNodes, acc);
const outgoers = getOutgoers(node, remainingNodes, acc);
const connectedEdges = getConnectedEdges([node], acc);
const remainingEdges = acc.filter((edge) => !connectedEdges.includes(edge));
const createdEdges = incomers.flatMap(({ id: source }) =>
outgoers.map(({ id: target }) => ({
id: `${source}->${target}`,
source,
target,
}))
);
remainingNodes = remainingNodes.filter((rn) => rn.id !== node.id);
return [...remainingEdges, ...createdEdges];
}, edges);
nodes = remainingNodes;
return true;
};
</script>
<SvelteFlow bind:nodes bind:edges {onbeforedelete} fitView />Components
组件
Controls
控制面板
svelte
<script lang="ts">
import { SvelteFlow, Controls } from '@xyflow/svelte';
</script>
<SvelteFlow bind:nodes bind:edges fitView>
<Controls showLock={false} showFitView={true} showZoom={true} />
</SvelteFlow>svelte
<script lang="ts">
import { SvelteFlow, Controls } from '@xyflow/svelte';
</script>
<SvelteFlow bind:nodes bind:edges fitView>
<Controls showLock={false} showFitView={true} showZoom={true} />
</SvelteFlow>MiniMap
迷你地图
svelte
<script lang="ts">
import { SvelteFlow, MiniMap } from '@xyflow/svelte';
</script>
<SvelteFlow bind:nodes bind:edges fitView>
<MiniMap />
</SvelteFlow>svelte
<script lang="ts">
import { SvelteFlow, MiniMap } from '@xyflow/svelte';
</script>
<SvelteFlow bind:nodes bind:edges fitView>
<MiniMap />
</SvelteFlow>Background
背景
svelte
<script lang="ts">
import { SvelteFlow, Background, BackgroundVariant } from '@xyflow/svelte';
</script>
<SvelteFlow bind:nodes bind:edges fitView>
<Background variant={BackgroundVariant.Dots} gap={12} size={1} />
</SvelteFlow>Variants: , ,
DotsLinesCrosssvelte
<script lang="ts">
import { SvelteFlow, Background, BackgroundVariant } from '@xyflow/svelte';
</script>
<SvelteFlow bind:nodes bind:edges fitView>
<Background variant={BackgroundVariant.Dots} gap={12} size={1} />
</SvelteFlow>背景样式选项:(点状)、(线状)、(十字线)
DotsLinesCrossPanel (Custom Controls)
面板(自定义控件)
svelte
<script lang="ts">
import { SvelteFlow, Panel } from '@xyflow/svelte';
</script>
<SvelteFlow bind:nodes bind:edges fitView>
<Panel position="top-left">
<button>Custom Button</button>
</Panel>
</SvelteFlow>Positions: , , , , ,
top-lefttop-centertop-rightbottom-leftbottom-centerbottom-rightsvelte
<script lang="ts">
import { SvelteFlow, Panel } from '@xyflow/svelte';
</script>
<SvelteFlow bind:nodes bind:edges fitView>
<Panel position="top-left">
<button>自定义按钮</button>
</Panel>
</SvelteFlow>面板位置选项:(左上角)、(顶部居中)、(右上角)、(左下角)、(底部居中)、(右下角)
top-lefttop-centertop-rightbottom-leftbottom-centerbottom-rightUtility Functions
工具函数
typescript
import {
getConnectedEdges,
getIncomers,
getOutgoers,
addEdge,
applyEdgeChanges,
applyNodeChanges
} from '@xyflow/svelte';
// Get edges connected to nodes
const connectedEdges = getConnectedEdges(nodes, edges);
// Get nodes that connect TO a node
const incomers = getIncomers(targetNode, nodes, edges);
// Get nodes that a node connects TO
const outgoers = getOutgoers(sourceNode, nodes, edges);
// Add edge with validation
edges = addEdge(connection, edges);typescript
import {
getConnectedEdges,
getIncomers,
getOutgoers,
addEdge,
applyEdgeChanges,
applyNodeChanges
} from '@xyflow/svelte';
// 获取与节点关联的边
const connectedEdges = getConnectedEdges(nodes, edges);
// 获取连接到目标节点的所有节点
const incomers = getIncomers(targetNode, nodes, edges);
// 获取目标节点连接到的所有节点
const outgoers = getOutgoers(sourceNode, nodes, edges);
// 带验证的添加边操作
edges = addEdge(connection, edges);Store Management (Alternative)
状态管理(替代方案)
For centralized state:
javascript
// store.svelte.js
let nodes = $state.raw([...]);
let edges = $state.raw([...]);
export const getNodes = () => nodes;
export const getEdges = () => edges;
export const setNodes = (newNodes) => nodes = newNodes;
export const setEdges = (newEdges) => edges = newEdges;svelte
<!-- Component.svelte -->
<script>
import { getNodes, getEdges, setNodes, setEdges } from './store.svelte.js';
</script>
<SvelteFlow bind:nodes={getNodes, setNodes} bind:edges={getEdges, setEdges} />适用于集中式状态管理场景:
javascript
// store.svelte.js
let nodes = $state.raw([...]);
let edges = $state.raw([...]);
export const getNodes = () => nodes;
export const getEdges = () => edges;
export const setNodes = (newNodes) => nodes = newNodes;
export const setEdges = (newEdges) => edges = newEdges;svelte
<!-- Component.svelte -->
<script>
import { getNodes, getEdges, setNodes, setEdges } from './store.svelte.js';
</script>
<SvelteFlow bind:nodes={getNodes, setNodes} bind:edges={getEdges, setEdges} />Export to Image
导出为图片
svelte
<script lang="ts">
import { useSvelteFlow } from '@xyflow/svelte';
const { toObject, getViewport } = useSvelteFlow();
async function downloadImage() {
const { toPng } = await import('html-to-image');
const dataUrl = await toPng(document.querySelector('.svelte-flow'));
const a = document.createElement('a');
a.setAttribute('download', 'flow.png');
a.setAttribute('href', dataUrl);
a.click();
}
</script>svelte
<script lang="ts">
import { useSvelteFlow } from '@xyflow/svelte';
const { toObject, getViewport } = useSvelteFlow();
async function downloadImage() {
const { toPng } = await import('html-to-image');
const dataUrl = await toPng(document.querySelector('.svelte-flow'));
const a = document.createElement('a');
a.setAttribute('download', 'flow.png');
a.setAttribute('href', dataUrl);
a.click();
}
</script>Performance Tips
性能优化建议
- Use for nodes and edges (prevents deep reactivity)
$state.raw - For large graphs (1000+ nodes), set and
elevateNodesOnSelect={false}elevateEdgesOnSelect={false} - Use and
minZoomto limit zoom levelsmaxZoom - Consider virtualization for extremely large graphs
- 对节点和边使用(避免深度响应式监听)
$state.raw - 对于大型图(1000+节点),设置和
elevateNodesOnSelect={false}elevateEdgesOnSelect={false} - 使用和
minZoom限制缩放级别maxZoom - 超大型图考虑使用虚拟化技术
Common Props
常用属性
svelte
<SvelteFlow
bind:nodes
bind:edges
nodeTypes={customNodeTypes}
edgeTypes={customEdgeTypes}
defaultEdgeOptions={{ animated: true, type: 'smoothstep' }}
connectionLineType={ConnectionLineType.Straight}
connectionMode={ConnectionMode.Loose}
fitView
minZoom={0.1}
maxZoom={2}
snapToGrid={true}
snapGrid={[15, 15]}
elevateNodesOnSelect={true}
elevateEdgesOnSelect={true}
deleteKeyCode="Delete"
selectionKeyCode="Shift"
multiSelectionKeyCode="Meta"
panOnScroll={true}
zoomOnScroll={true}
zoomOnDoubleClick={true}
zoomOnPinch={true}
panOnDrag={true}
/>svelte
<SvelteFlow
bind:nodes
bind:edges
nodeTypes={customNodeTypes}
edgeTypes={customEdgeTypes}
defaultEdgeOptions={{ animated: true, type: 'smoothstep' }}
connectionLineType={ConnectionLineType.Straight}
connectionMode={ConnectionMode.Loose}
fitView
minZoom={0.1}
maxZoom={2}
snapToGrid={true}
snapGrid={[15, 15]}
elevateNodesOnSelect={true}
elevateEdgesOnSelect={true}
deleteKeyCode="Delete"
selectionKeyCode="Shift"
multiSelectionKeyCode="Meta"
panOnScroll={true}
zoomOnScroll={true}
zoomOnDoubleClick={true}
zoomOnPinch={true}
panOnDrag={true}
/>