Vue‑Flow是对标React‑Flow(@xyflow/react) 的Vue3图编辑库,用来做DAG工作流、Agent流程图、拓扑图。官网:
https://vueflow.dev/guide技术栈:Vue3 + Vite + JavaScript
一、什么是 Vue‑Flow
Vue‑Flow 是社区维护的图编辑组件库,核心能力:节点拖拽、画布平移缩放、锚点连线、自定义节点。
底层逻辑:两份普通JS数组驱动整个画布
nodes:节点数组,画布上每一个方块edges:连线数组,节点和节点之间的连线
核心公式:修改
nodes / edges→ 画布自动响应更新。
字段基础结构
node 节点
js
{
id: "唯一字符串",
position: {x: 数字, y:数字}, //画布坐标
type: "default", //default 使用内置默认节点;自定义节点填注册的type名称
data: {} //业务自定义数据,节点显示的内容全部放这里
}
edge 连线
js
{
id:"连线唯一ID",
source:"源节点id",
target:"目标节点id"
}
二、快速搭建最小 Demo
1. 创建项目
bash
pnpm create vite flow‑demo --template vue
cd flow‑demo
pnpm install
2. 安装核心依赖
扩展包
@vue‑flow/background等新版存在导出问题,初学先不用。
bash
pnpm add @vue-flow/core
3. 最小可运行代码 App.vue
html
<template>
<div class="wrap">
<VueFlow
v-model:nodes="nodes"
v-model:edges="edges"
@connect="handleConnect"
></VueFlow>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { VueFlow, useVueFlow } from '@vue-flow/core'
// 必须导入核心样式,否则节点、锚点丢失
import '@vue-flow/core/dist/style.css'
import '@vue-flow/core/dist/theme-default.css'
const flow = useVueFlow()
const nodes = ref([
{
id: 'a',
position: { x: 50, y: 100 },
type: 'default',
data: { label: '节点1' }
},
{
id: 'b',
position: { x: 300, y: 100 },
type: 'default',
data: { label: '节点2' }
}
])
const edges = ref([
{ id: 'e‑a‑b', source: 'a', target: 'b' }
])
// 用户拖拽锚点生成连线触发
const handleConnect = (info) => {
console.log('新建连线', info)
}
</script>
<style scoped>
.wrap {
width: 100%;
height: 500px; /* ⚠️画布容器必须写固定高度,不会自动撑开 */
border:1px solid #ccc;
}
</style>
运行 pnpm dev
可以做的操作:拖拽节点、画布平移、滚轮缩放、拖拽锚点连线。
三、基础能力扩展
1. 程序控制:新增 / 删除节点
nodes、edges 本质就是数组,直接操作数组即可更新画布。
js
//新增节点
const addNode = ()=>{
const newId = "node‑" + Date.now()
nodes.value.push({
id:newId,
position:{x:100,y:200},
type:"default",
data:{label:"新节点"}
})
}
//删除节点,同时清理关联连线
const deleteNodeById = (delId)=>{
nodes.value = nodes.value.filter(n=>n.id!==delId)
edges.value = edges.value.filter(e=> e.source!==delId && e.target!==delId)
}
2. 异步后端接口渲染画布(真实业务高频)
场景:从后端拿到 nodes、edges 数据渲染拓扑图。
关键点:后端返回数据结构必须匹配 Vue‑Flow 的 node/edge 格式;拿到数据直接赋值覆盖 ref;加载完调用fitView()自动适配视口。
js
//模拟后端请求
async function loadGraph(){
// const res = await axios.get("/api/graph")
//模拟接口返回
const remote = {
nodes:[
{id:"r1",position:{x:50,y:80},type:"default",data:{label:"Agent‑A"}}
],
edges:[]
}
nodes.value = remote.nodes
edges.value = remote.edges
//自动缩放画布,把全部节点展示在视口中
flow.fitView({padding:30})
}
3. 常用事件监听
html
<VueFlow
@connect="handleConnect"
@node‑click="onNodeClick"
@node‑drag‑stop="onDragStop"
@edge‑click="onEdgeClick"
/>
@connect:拖拽锚点生成连线@node‑click:点击节点@node‑drag‑stop:节点拖拽结束,适合保存节点新坐标回后端@edge‑click:点击连线
四、生产环境性能优化
节点数量少(<50)不需要优化;上百个节点才会出现卡顿。
- 使用 shallowRef,减少深度响应式开销
ref会递归代理数组内部每一个 node 对象,大量节点性能差。
shallowRef仅对数组外层做响应式,不代理内部对象。
注意:shallowRef 禁止直接 push,必须整体赋值替换数组。
js
import { shallowRef } from 'vue'
const nodes = shallowRef([])
// ✅正确
nodes.value = [...nodes.value, newNode]
// ❌禁止
nodes.value.push(newNode)
- 开启虚拟渲染:上千节点场景
只渲染视口内可见节点,不在视口的节点不渲染 DOM。
html
<VueFlow :virtualization="true"/>
限制:开启虚拟渲染,节点尽量使用固定高度,动态高度会有 bug。
- 业务层面优化
- 不要在
node‑drag(拖拽过程高频事件)中做请求、复杂计算,放到node‑drag‑stop。 - 批量更新 nodes/edges,一次性赋值,不要循环逐个 push。
- 大量连线避免开启
animated:true流动动画,消耗 CPU。 - 事件回调不要写内联箭头函数,避免每次渲染生成新引用触发重渲染。
五、常见踩坑清单
- 外层容器必须设置 height,VueFlow 不会自动撑开高度,否则画布看不见。
- 必须导入 core 的
style.css,否则锚点、节点样式丢失。 - node.id、edge.id 必须全局唯一,重复 id 会出现诡异 bug。
<Handle>连接桩只能写在自定义节点组件内部,不能直接写在画布根模板。- 新版扩展包
@vue‑flow/background直接导入 dist/style.css 会报 exports 导出错误。
六、业务场景:AI Agent 渗透拓扑图
- 自定义节点 + Handle 连接桩,渲染多 Agent 协作 DAG 拓扑。
- 接口拆分:图谱静态结构、任务渗透进度轮询两个接口,按需更新画布,避免大体积数据频繁传输。
- 对图编辑库做分包懒加载,不阻塞首屏登录页。