极致的灵活度满足工程美学:用Vue Flow绘制一个完美流程图

简介: 本文介绍了使用 Vue Flow 绘制流程图的方法与技巧。Vue Flow 是一个灵活强大的工具,适合自定义复杂的流程图。文章从环境要求(Node.js v20+ 和 Vue 3.3+)、基础入门案例、自定义功能(节点与连线的定制、事件处理)到实际案例全面解析其用法。重点强调了 Vue Flow 的高度灵活性,虽然预定义内容较少,但提供了丰富的 API 支持深度定制。同时,文中还分享了关于句柄(handles)的使用方法,以及如何解决官网复杂案例无法运行的问题。最后通过对比 mermaid,总结 Vue Flow 更适合需要高度自定义和复杂需求的场景,并附带多个相关技术博客链接供进一步学习。


极致的灵活度满足工程美学:用Vue Flow绘制一个完美流程图

       Vue Flow的入门门槛要高不少,毕竟找不到中文文档,反正我是不太喜欢看纯英文的文档的。不过没关系,看完这篇还用不好Vue Flow请来找我麻烦。

一、环境要求

       Node.js v20 或更高版本

       Vue 3.3 或更高版本

       前者,没有装nvm建议装一个nvm,noder没有nvm是行不通的。后者意味着Vue Flow不支持Vue2,据说有些辅助组件可以让部分功能在Vue中使用。

Tips:

       如果把我的源码抄过去都跑不动,要么是依赖没有装好,要么是依赖的版本不对,Vue Flow的版本更迭是会伴随着API的更迭的,所以可以安装特定版本的Vue Flow来解决问题。我的Vue Flow版本是:v1.39.0

package.json:

   "@vue-flow/background": "^1.3.0",

   "@vue-flow/controls": "^1.1.2",

   "@vue-flow/core": "^1.38.5",

   "@vue-flow/minimap": "^1.5.0",

二、初识Vue Flow

2.1、安装Vue Flow

       根据自己的包管理器安装

不习惯用add就用install,也没问题

npm add @vue-flow/core

pnpm add @vue-flow/core

yarn add @vue-flow/core

2.2、Vue Flow构成

       在 Vue Flow 中,图由节点和边组成,每个节点或边都需要一个唯一的ID,节点还需要 XY 位置,而边需要 source 和 target 节点 ID,具体参照下方示例中的数据。

2.3、一个小坑

       我最开始在官网上找案例的时候,发现除了教程案例以外的所有多组件案例都跑不动。不要只顾着怀疑自己,因为官网上的案例就是跑不动的。

       原因在于:

为了确保 Vue Flow 正确显示,请确保包含必要的样式。

/* these are necessary styles for vue flow */

@import '@vue-flow/core/dist/style.css';

/* this contains the default theme, these are optional styles */

@import '@vue-flow/core/dist/theme-default.css';

        是的,官网复杂案例全都没有添加这两行代码,估计是写文档的程序员被push的太狠了,抓紧写了几个案例就把代码放上来,没有意识到自己是全局引入(我猜的)了这两行代码,并且没有做任何提示。

       但对于我们来说,只是为了画几个流程图的话,全局引入只会让代码变得冗余。局部引入就够了,把这两行放到style中官网的复杂案例就都可以执行了。

2.4、入门案例

       请注意,这个代码确定能跑,如果跑不动请先检查依赖。

       结构非常简单,定义了一个节点数据数组,一个连线数据数组,然后绑定给VueFlow组件即可。

<script setup>
import { ref } from "vue";
import { VueFlow } from "@vue-flow/core";
const nodes = ref([
  // an input node, specified by using `type: 'input'`
  {
    id: "1",
    type: "input",
    position: { x: 250, y: 5 },
    // all nodes can have a data object containing any data you want to pass to the node
    // a label can property can be used for default nodes
    data: { label: "Node 1" },
  },
  // default node, you can omit `type: 'default'` as it's the fallback type
  {
    id: "2",
    position: { x: 100, y: 100 },
    data: { label: "Node 2" },
  },
  // An output node, specified by using `type: 'output'`
  {
    id: "3",
    type: "output",
    position: { x: 400, y: 200 },
    data: { label: "Node 3" },
  },
  // this is a custom node
  // we set it by using a custom type name we choose, in this example `special`
  // the name can be freely chosen, there are no restrictions as long as it's a string
  {
    id: "4",
    type: "special", // <-- this is the custom node type name
    position: { x: 800, y: 200 },
    data: {
      label: "Node 4",
      hello: "world",
    },
  },
]);
const edges = ref([
  // default bezier edge
  // consists of an edge id, source node id and target node id
  {
    id: "e1->2",
    source: "1",
    target: "2",
  },
  // set `animated: true` to create an animated edge path
  {
    id: "e2->3",
    source: "2",
    target: "3",
    animated: true,
  },
  // a custom edge, specified by using a custom type name
  // we choose `type: 'special'` for this example
  {
    id: "e3->4",
    type: "special",
    source: "3",
    target: "4",
    // all edges can have a data object containing any data you want to pass to the edge
    data: {
      hello: "world",
    },
  },
]);
</script>
<template>
  <VueFlow :nodes="nodes" :edges="edges" style="height: 100vh; width: 100vw">
  </VueFlow>
</template>
<style>
/* import the necessary styles for Vue Flow to work */
@import "@vue-flow/core/dist/style.css";
/* import the default theme, this is optional but generally recommended */
@import "@vue-flow/core/dist/theme-default.css";
</style>

image.gif

三、Vue Flow优秀的自定义功能

3.1、引入

       首先,预定义的配置项就不介绍了,上面的案例基本就是全部的预定义配置了,这一点和mermaid完全无法相比,但是你要用预定义的配置为什么不直接用mermaid呢?那个更简单清爽。

       Vue Flow的特点就是,什么都可以要,什么都要自己写。

       相比于mermaid来说,Vue Flow接受的数据更复杂冗长,并且预定义的内容极少,连默认的布局都没有(节点通过position控制位置,很容易重叠)。但是Vue Flow提供的自定义API非常丰富并且强大。

3.2、节点与连线的自定义

       自定义三步走:打样取名做替换。

①打样(做模板)

       做一个节点或者连线的模版,比如我需要一个长得像太阳的节点,那么我就写一个组件,然后把这个组件做成一个太阳的样子。

②模版取名

       给自己做的模版取一个名字,比如就叫sun,然后将想要使用这个模版的节点的type属性全部改成"sun"。

③替换模版

       在<VueFlow>标签之间添加如下代码,主要关注插槽名称“#node-sun”意味着所有type为sun的节点都使用这个插槽的模版。比如我设计了一种连线取名为darge,那么插槽名就是“#edge-darge”。

// 在script中引入
import sunNode from "./sunNode.vue";
<template>
  <div class="layout-flow">
    <VueFlow
      :nodes="nodes"
      :edges="edges"
      @nodes-initialized="layoutGraph('TB')"
    >
      <template #node-sun="props">
        <div class="sunNode">
          <span>{{ props.data.label }}</span>
        </div>
      </template>
    </VueFlow>
  </div>
</template>

image.gif

3.3、节点与连线的事件

       该有的事件应有尽有,不得不佩服Vue Flow的完善与强大。

①节点事件

       这是我比较建议的用法,比较简洁

<script setup>
import { ref } from 'vue'
import { VueFlow } from '@vue-flow/core'
const nodes = ref([
  {
    id: '1',
    data: { label: 'Node 1' },
    position: { x: 50, y: 50 },
  },
])
  
function logEvent(name, data) {
  console.log(name, data)
}
</script>
<template>
  <!-- bind listeners to the event handlers -->
  <VueFlow
    :nodes="nodes"
    @node-drag-start="logEvent('drag start', $event)"
    @node-drag="logEvent('drag', $event)"
    @node-drag-stop="logEvent('drag stop', $event)"
    @node-click="logEvent('click', $event)"
    @node-double-click="logEvent('dblclick', $event)"
    @node-contextmenu="logEvent('contextmenu', $event)"
    @node-mouse-enter="logEvent('mouseenter', $event)"
    @node-mouse-leave="logEvent('mouseleave', $event)"
    @node-mouse-move="logEvent('mousemove', $event)"
  />
</template>

image.gif

       也可以使用useVueFlow这个API,将这些事件转化为钩子,我觉得优势不明显,写起来也冗余。

<script setup>
import { ref } from 'vue'  
import { VueFlow, useVueFlow } from '@vue-flow/core'
// useVueFlow provides access to the event handlers
const { 
  onNodeDragStart, 
  onNodeDrag,
  onNodeDragStop, 
  onNodeClick, 
  onNodeDoubleClick, 
  onNodeContextMenu, 
  onNodeMouseEnter, 
  onNodeMouseLeave, 
  onNodeMouseMove 
} = useVueFlow()
  
const nodes = ref([
  {
    id: '1',
    data: { label: 'Node 1' },
    position: { x: 50, y: 50 },
  },
])
  
// bind listeners to the event handlers
onNodeDragStart((event) => {
  console.log('Node drag started', event)
})
onNodeDrag((event) => {
  console.log('Node dragged', event)
})
onNodeDragStop((event) => {
  console.log('Node drag stopped', event)
})
  
// ... and so on  
</script>
<template>
  <VueFlow :nodes="nodes" />
</template>

image.gif

②连线事件

       连线事件也可以使用useVueFlow这个API,用法和上方一样,我就不赘述了,这里展示一下有哪些事件API。

<script setup>
import { ref } from 'vue'
import { VueFlow } from '@vue-flow/core'
const nodes = ref([
  {
    id: '1',
    position: { x: 50, y: 50 },
    data: { label: 'Node 1', },
  },
  {
    id: '2',
    position: { x: 50, y: 250 },
    data: { label: 'Node 2', },
  },
])
const edges = ref([
  {
    id: 'e1->2',
    source: '1',
    target: '2',
  },
])
  
function logEvent(eventName, data) {
  console.log(eventName, data)
}
</script>
<template>
  <VueFlow
    :nodes="nodes"
    :edges="edges"
    @edge-click="logEvent('edge clicked', $event)"
    @edge-double-click="logEvent('edge double clicked', $event)"
    @edge-context-menu="logEvent('edge context menu', $event)"
    @edge-mouse-enter="logEvent('edge mouse enter', $event)"
    @edge-mouse-leave="logEvent('edge mouse leave', $event)"
    @edge-mouse-move="logEvent('edge mouse move', $event)"
    @edge-update-start="logEvent('edge update start', $event)"
    @edge-update="logEvent('edge update', $event)"
    @edge-update-end="logEvent('edge update end', $event)"
  />
</template>

image.gif

3.4、句柄(handles)

       句柄是通常放置在节点边界上的小圆圈。它们用于通过将连接线从一个手柄拖动到另一个手柄来将节点连接在一起,从而在节点之间形成连接(边缘)。句柄是 VueFlow 的重要组成部分,因为它们是用户在节点之间创建边的主要交互点。如果没有句柄,基本上不可能在节点之间创建边,因为句柄用于计算边的源点和目标点。

       简单来说,就是两个节点连接需要通过句柄,如果没有句柄就无法连接。句柄就是连线两端的端点。常用在自定义节点的组件中,用来指定连线的起始点。

       句柄handdle需要设置两个属性,一个是类型(source/target),一个是位置(Top、Right、Bottom、Left),示例如下:

<script setup>
import { Handle } from '@vue-flow/core'
  
defineProps(['id', 'sourcePosition', 'targetPosition', 'data'])
</script>
<template>
  <Handle type="source" :position="sourcePosition" />
  
  <span>{{ data.label }}</span>
  
  <Handle type="target" :position="targetPosition" />
</template>

image.gif

四、一个优质的案例

       案例具体代码请访问:Layouting | Vue Flow

       将所有组件代码复制到您的项目中即可,请注意相对路径关系,如果您没有全局引入2.3部分的内容,请在style中添加:

<style>
@import "@vue-flow/core/dist/style.css";
@import "@vue-flow/core/dist/theme-default.css";
// ...其他的css样式
</style>

image.gif

五、总结

       相比于mermaid,Vue Flow将注意力更多的转到灵活自定义的接口而不是大量的预定义模版,“什么都可以有,什么都要自己写。”是Vue Flow的典型特征。如果您想发挥自己天马行空的想象力,或者满足复杂的项目流程需求,Vue Flow一定能满足您的预期。

       博客不应该只有代码和解决方案,重点应该在于给出解决方案的同时分享思维模式,只有思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~

       更多优质内容,请关注:

JS语法与Vue开发:

       Vue 性能革命:揭秘前端优化的终极技巧

       属性描述符初探——Vue实现数据劫持的基础

       你真的会使用Vue3的onMounted钩子函数吗?Vue3中onMounted的用法详解

       最细最有条理解析:事件循环(消息循环)是什么?进程与线程的定义、关系与差异

       路由通配符,小小的字符有大大的作用,你真的熟悉吗?

       管理数据必备!侦听器watch用法详解

       什么是深拷贝?深拷贝和浅拷贝有什么区别

       对象数据的读取,看这一篇就够了!

       通过array.every()实现数据验证、权限检查和一致性检查,array.some与array.every的区别

       通过array.some()实现权限检查、表单验证、库存管理、内容审查和数据处理

       通过array.map()实现数据转换、创建派生数组、异步数据流处理、搜索和过滤等需求

       通过array.reduce()实现数据汇总、条件筛选和映射、对象属性的扁平化、转换数据格式等

       通过array.filter()实现数组的数据筛选、数据清洗和链式调用

       多维数组操作,不要再用遍历循环foreach了,来试试数组展平的小妙招!

       别再用双层遍历循环来做新旧数组对比,寻找新增元素了!

       shpfile转GeoJSON且控制转化精度;如何获取GeoJSON?GeoJson结构详解

       Mapbox添加行政区矢量图层、分级设色图层、自定义鼠标悬浮框、添加天地图底图等

Element plus拓展:

       通过el-tree自定义渲染网页版工作目录,实现鼠标悬浮显示完整名称等

       el-table实现动态数据的实时排序,一篇文章讲清楚elementui的表格排序功能

       el-table中如何添加渐变色带、多色色带

相关文章
|
7月前
|
JavaScript
Vue中如何实现兄弟组件之间的通信
在Vue中,兄弟组件可通过父组件中转、事件总线、Vuex/Pinia或provide/inject实现通信。小型项目推荐父组件中转或事件总线,大型项目建议使用Pinia等状态管理工具,确保数据流清晰可控,避免内存泄漏。
623 2
|
6月前
|
缓存 JavaScript
vue中的keep-alive问题(2)
vue中的keep-alive问题(2)
537 137
|
10月前
|
人工智能 JavaScript 算法
Vue 中 key 属性的深入解析:改变 key 导致组件销毁与重建
Vue 中 key 属性的深入解析:改变 key 导致组件销毁与重建
1059 0
|
10月前
|
JavaScript UED
用组件懒加载优化Vue应用性能
用组件懒加载优化Vue应用性能
|
9月前
|
人工智能 JSON JavaScript
VTJ.PRO 首发 MasterGo 设计智能识别引擎,秒级生成 Vue 代码
VTJ.PRO发布「AI MasterGo设计稿识别引擎」,成为全球首个支持解析MasterGo原生JSON文件并自动生成Vue组件的AI工具。通过双引擎架构,实现设计到代码全流程自动化,效率提升300%,助力企业降本增效,引领“设计即生产”新时代。
642 1
|
9月前
|
JavaScript 安全
在 Vue 中,如何在回调函数中正确使用 this?
在 Vue 中,如何在回调函数中正确使用 this?
442 0
|
10月前
|
JavaScript 前端开发 UED
Vue 表情包输入组件实现代码及详细开发流程解析
这是一篇关于 Vue 表情包输入组件的使用方法与封装指南的文章。通过安装依赖、全局注册和局部使用,可以快速集成表情包功能到 Vue 项目中。文章还详细介绍了组件的封装实现、高级配置(如自定义表情列表、主题定制、动画效果和懒加载)以及完整集成示例。开发者可根据需求扩展功能,例如 GIF 搜索或自定义表情上传,提升用户体验。资源链接提供进一步学习材料。
625 1
|
12月前
|
JavaScript
vue实现任务周期cron表达式选择组件
vue实现任务周期cron表达式选择组件
1342 4
|
11月前
|
JavaScript 数据可视化 前端开发
基于 Vue 与 D3 的可拖拽拓扑图技术方案及应用案例解析
本文介绍了基于Vue和D3实现可拖拽拓扑图的技术方案与应用实例。通过Vue构建用户界面和交互逻辑,结合D3强大的数据可视化能力,实现了力导向布局、节点拖拽、交互事件等功能。文章详细讲解了数据模型设计、拖拽功能实现、组件封装及高级扩展(如节点类型定制、连接样式优化等),并提供了性能优化方案以应对大数据量场景。最终,展示了基础网络拓扑、实时更新拓扑等应用实例,为开发者提供了一套完整的实现思路和实践经验。
1439 78