SvelteFlow基础知识Skill svelteflow-fundamentals

这个技能用于构建可自定义的节点基础编辑器和交互式图表,适用于Svelte应用中的流程基础界面。涵盖核心概念、设置和常见模式,关键词包括Svelte Flow、节点基础、流程图、前端开发、交互式UI,适合SEO搜索。

前端开发 0 次安装 0 次浏览 更新于 3/25/2026

name: svelteflow-fundamentals user-invocable: false description: 使用Svelte Flow构建节点基础的UI、流程图、工作流编辑器或交互式图表。涵盖设置、节点、边缘、控件和交互性。 allowed-tools:

  • Bash
  • Read

Svelte Flow 基础知识

使用Svelte Flow构建可自定义的节点基础编辑器和交互式图表。 这个技能涵盖核心概念、设置和创建Svelte应用中流程基础界面的常见模式。

安装

# npm
npm install @xyflow/svelte

# pnpm
pnpm add @xyflow/svelte

# yarn
yarn add @xyflow/svelte

基本设置

<script lang="ts">
  import {
    SvelteFlow,
    Controls,
    Background,
    MiniMap,
    type Node,
    type Edge,
  } from '@xyflow/svelte';
  import { writable } from 'svelte/store';

  import '@xyflow/svelte/dist/style.css';

  const nodes = writable<Node[]>([
    {
      id: '1',
      type: 'input',
      data: { label: '开始' },
      position: { x: 250, y: 0 },
    },
    {
      id: '2',
      data: { label: '处理' },
      position: { x: 250, y: 100 },
    },
    {
      id: '3',
      type: 'output',
      data: { label: '结束' },
      position: { x: 250, y: 200 },
    },
  ]);

  const edges = writable<Edge[]>([
    { id: 'e1-2', source: '1', target: '2' },
    { id: 'e2-3', source: '2', target: '3' },
  ]);
</script>

<div style="height: 100vh; width: 100vw;">
  <SvelteFlow {nodes} {edges} fitView>
    <Controls />
    <Background />
    <MiniMap />
  </SvelteFlow>
</div>

使用存储管理状态

<script lang="ts">
  import { SvelteFlow, type Node, type Edge } from '@xyflow/svelte';
  import { writable, derived } from 'svelte/store';

  // 可写存储用于反应式状态
  const nodes = writable<Node[]>([
    { id: '1', data: { label: '节点 1' }, position: { x: 0, y: 0 } },
    { id: '2', data: { label: '节点 2' }, position: { x: 200, y: 100 } },
  ]);

  const edges = writable<Edge[]>([
    { id: 'e1-2', source: '1', target: '2' },
  ]);

  // 派生存储用于计算值
  const nodeCount = derived(nodes, ($nodes) => $nodes.length);
  const edgeCount = derived(edges, ($edges) => $edges.length);

  // 添加新节点
  function addNode() {
    const id = `node-${Date.now()}`;
    nodes.update((n) => [
      ...n,
      {
        id,
        data: { label: `节点 ${$nodeCount + 1}` },
        position: { x: Math.random() * 300, y: Math.random() * 300 },
      },
    ]);
  }

  // 更新节点标签
  function updateNodeLabel(id: string, label: string) {
    nodes.update((n) =>
      n.map((node) =>
        node.id === id ? { ...node, data: { ...node.data, label } } : node
      )
    );
  }

  // 删除节点
  function deleteNode(id: string) {
    nodes.update((n) => n.filter((node) => node.id !== id));
    // 同时移除连接的边缘
    edges.update((e) => e.filter((edge) => edge.source !== id && edge.target !== id));
  }
</script>

<div>
  <p>节点: {$nodeCount} | 边缘: {$edgeCount}</p>
  <button on:click={addNode}>添加节点</button>
</div>

<SvelteFlow {nodes} {edges} fitView />

节点类型

内置节点类型

<script lang="ts">
  import { SvelteFlow, type Node } from '@xyflow/svelte';
  import { writable } from 'svelte/store';

  const nodes = writable<Node[]>([
    // 输入节点 - 只有源句柄
    {
      id: '1',
      type: 'input',
      data: { label: '输入节点' },
      position: { x: 0, y: 0 },
    },
    // 默认节点 - 有源句柄和目标句柄
    {
      id: '2',
      type: 'default',
      data: { label: '默认节点' },
      position: { x: 0, y: 100 },
    },
    // 输出节点 - 只有目标句柄
    {
      id: '3',
      type: 'output',
      data: { label: '输出节点' },
      position: { x: 0, y: 200 },
    },
  ]);
</script>

节点配置

import { Position, type Node } from '@xyflow/svelte';

const node: Node = {
  id: 'unique-id',
  type: 'default',
  position: { x: 100, y: 100 },
  data: { label: '我的节点', customProp: 'value' },
  // 可选属性
  style: 'background-color: #f0f0f0;',
  class: 'custom-node',
  sourcePosition: Position.Right,
  targetPosition: Position.Left,
  draggable: true,
  selectable: true,
  connectable: true,
  deletable: true,
  hidden: false,
  selected: false,
  dragging: false,
  zIndex: 0,
  extent: 'parent',
  parentId: 'parent-node-id',
  expandParent: true,
};

边缘类型和配置

<script lang="ts">
  import { SvelteFlow, MarkerType, type Edge } from '@xyflow/svelte';
  import { writable } from 'svelte/store';

  const edges = writable<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',
      label: '连接',
      style: 'stroke: #333; stroke-width: 2px;',
      animated: true,
      markerEnd: {
        type: MarkerType.ArrowClosed,
        color: '#333',
      },
    },
  ]);
</script>

事件处理

<script lang="ts">
  import { SvelteFlow, type Node, type Edge } from '@xyflow/svelte';
  import { writable } from 'svelte/store';

  const nodes = writable<Node[]>([]);
  const edges = writable<Edge[]>([]);

  // 节点事件
  function handleNodeClick(event: CustomEvent<{ node: Node }>) {
    console.log('节点点击:', event.detail.node);
  }

  function handleNodeDoubleClick(event: CustomEvent<{ node: Node }>) {
    console.log('节点双击:', event.detail.node);
  }

  function handleNodeDragStart(event: CustomEvent<{ node: Node }>) {
    console.log('拖动开始:', event.detail.node.id);
  }

  function handleNodeDragStop(event: CustomEvent<{ node: Node }>) {
    console.log('拖动停止:', event.detail.node.position);
  }

  // 边缘事件
  function handleEdgeClick(event: CustomEvent<{ edge: Edge }>) {
    console.log('边缘点击:', event.detail.edge);
  }

  // 连接事件
  function handleConnect(event: CustomEvent<{ connection: any }>) {
    const { connection } = event.detail;
    edges.update((e) => [
      ...e,
      {
        id: `${connection.source}-${connection.target}`,
        source: connection.source,
        target: connection.target,
      },
    ]);
  }

  // 选择事件
  function handleSelectionChange(
    event: CustomEvent<{ nodes: Node[]; edges: Edge[] }>
  ) {
    console.log('选择的节点:', event.detail.nodes);
    console.log('选择的边缘:', event.detail.edges);
  }
</script>

<SvelteFlow
  {nodes}
  {edges}
  on:nodeclick={handleNodeClick}
  on:nodedoubleclick={handleNodeDoubleClick}
  on:nodedragstart={handleNodeDragStart}
  on:nodedragstop={handleNodeDragStop}
  on:edgeclick={handleEdgeClick}
  on:connect={handleConnect}
  on:selectionchange={handleSelectionChange}
  fitView
/>

插件组件

背景

<script>
  import { SvelteFlow, Background, BackgroundVariant } from '@xyflow/svelte';
</script>

<SvelteFlow {nodes} {edges}>
  <!-- 点图案 -->
  <Background variant={BackgroundVariant.Dots} gap={12} size={1} />

  <!-- 线图案 -->
  <Background variant={BackgroundVariant.Lines} gap={20} />

  <!-- 交叉图案 -->
  <Background variant={BackgroundVariant.Cross} gap={25} />

  <!-- 自定义颜色 -->
  <Background color="#aaa" gap={16} />
</SvelteFlow>

控件

<script>
  import { SvelteFlow, Controls } from '@xyflow/svelte';
</script>

<SvelteFlow {nodes} {edges}>
  <Controls
    showZoom={true}
    showFitView={true}
    showInteractive={true}
    position="bottom-left"
  />
</SvelteFlow>

迷你地图

<script>
  import { SvelteFlow, MiniMap } from '@xyflow/svelte';
</script>

<SvelteFlow {nodes} {edges}>
  <MiniMap
    nodeColor={(node) => {
      switch (node.type) {
        case 'input':
          return '#0041d0';
        case 'output':
          return '#ff0072';
        default:
          return '#1a192b';
      }
    }}
    nodeStrokeWidth={3}
    zoomable
    pannable
  />
</SvelteFlow>

面板

<script>
  import { SvelteFlow, Panel } from '@xyflow/svelte';
</script>

<SvelteFlow {nodes} {edges}>
  <Panel position="top-left">
    <button on:click={saveFlow}>保存</button>
    <button on:click={restoreFlow}>恢复</button>
  </Panel>

  <Panel position="top-right">
    <p>节点数量: {$nodes.length}</p>
  </Panel>
</SvelteFlow>

使用useSvelteFlow控制视口

<script lang="ts">
  import { SvelteFlow, useSvelteFlow, Panel } from '@xyflow/svelte';

  const { zoomIn, zoomOut, fitView, setCenter, getViewport } = useSvelteFlow();

  function handleFitView() {
    fitView({ padding: 0.2 });
  }

  function handleCenter() {
    setCenter(0, 0, { zoom: 1 });
  }

  function logViewport() {
    const viewport = getViewport();
    console.log('当前视口:', viewport);
  }
</script>

<SvelteFlow {nodes} {edges}>
  <Panel position="top-left">
    <button on:click={() => zoomIn()}>放大</button>
    <button on:click={() => zoomOut()}>缩小</button>
    <button on:click={handleFitView}>适应视图</button>
    <button on:click={handleCenter}>居中</button>
    <button on:click={logViewport}>记录视口</button>
  </Panel>
</SvelteFlow>

节点操作

<script lang="ts">
  import { SvelteFlow, useSvelteFlow, type Node } from '@xyflow/svelte';
  import { writable } from 'svelte/store';

  const nodes = writable<Node[]>([]);
  const edges = writable<Edge[]>([]);

  const { getNodes, setNodes, addNodes, deleteElements } = useSvelteFlow();

  function addNewNode() {
    const id = `node-${Date.now()}`;
    addNodes([
      {
        id,
        data: { label: '新节点' },
        position: { x: Math.random() * 300, y: Math.random() * 300 },
      },
    ]);
  }

  function deleteSelectedNodes() {
    const selectedNodes = getNodes().filter((node) => node.selected);
    deleteElements({ nodes: selectedNodes });
  }

  function updateAllLabels(prefix: string) {
    setNodes((nodes) =>
      nodes.map((node, index) => ({
        ...node,
        data: { ...node.data, label: `${prefix} ${index + 1}` },
      }))
    );
  }
</script>

保存和恢复状态

<script lang="ts">
  import { SvelteFlow, useSvelteFlow, Panel, type Viewport } from '@xyflow/svelte';
  import { writable, get } from 'svelte/store';

  const nodes = writable<Node[]>([]);
  const edges = writable<Edge[]>([]);

  const { toObject, setViewport } = useSvelteFlow();

  function saveFlow() {
    const flow = toObject();
    localStorage.setItem('flow', JSON.stringify(flow));
    console.log('流程已保存');
  }

  function restoreFlow() {
    const saved = localStorage.getItem('flow');
    if (saved) {
      const flow = JSON.parse(saved);
      nodes.set(flow.nodes || []);
      edges.set(flow.edges || []);
      if (flow.viewport) {
        setViewport(flow.viewport);
      }
      console.log('流程已恢复');
    }
  }
</script>

<SvelteFlow {nodes} {edges}>
  <Panel position="top-right">
    <button on:click={saveFlow}>保存</button>
    <button on:click={restoreFlow}>恢复</button>
  </Panel>
</SvelteFlow>

连接验证

<script lang="ts">
  import { SvelteFlow, type IsValidConnection } from '@xyflow/svelte';

  const isValidConnection: IsValidConnection = (connection) => {
    // 防止自连接
    if (connection.source === connection.target) {
      return false;
    }

    // 添加自定义验证逻辑
    return true;
  };
</script>

<SvelteFlow {nodes} {edges} {isValidConnection} />

何时使用此技能

使用 svelteflow-fundamentals 当您需要:

  • 使用Svelte构建工作流构建器
  • 创建数据管道可视化
  • 设计状态机图
  • 构建聊天机器人对话流
  • 创建组织图
  • 构建机器学习管道可视化器
  • 在Svelte应用中创建交互式决策树

最佳实践

  • 使用Svelte存储管理反应式流状态
  • 保持节点组件专注和可重用
  • 使用TypeScript确保类型安全
  • 在派生存储中记忆昂贵的计算
  • 使用CSS变量进行主题化
  • 为高级用户添加键盘快捷键
  • 实现撤销/重做以提高用户体验
  • 在初始渲染时使用fitView()

资源