【第2212期】滴滴开源LogicFlow:专注流程可视化的前端框架
前⾔
滴滴开源的⼀款流程化可视化框架,有兴趣可以了解下。今⽇前端早读课⽂章由@LogicFlow官⽅号投稿分享。
背景
其次,各业务系统虽然都需要应⽤流程可视化技术,但需求各不相同。有的对流程图的要求⽐较简单,图的数据格式也简单,⽽有的需要按照 BPMN 的规范来绘制流程图,对于定制化的要求较⾼。我们调研了市⾯上相关的框架(BPMN.js、X6、Jsplumb、G6-editor),均存在不满⾜的场景,技术栈统⼀的成本很⾼。具体表现在:
•BMPN.js、Jsplumb 的拓展能⼒不⾜,⾃定义节点⽀持成本很⾼;只能全量引⼊,各系统⽆法按需引⼊
•与后端配套的流程引擎适配,成本较⾼。均不⽀持数据转换、不⽀持流程的校验等业务定制需求;
•⽂档、⽰例不健全。X6 和 BPMN 的⽂档不健全,⽰例少(2020 初调研结论)。
因此,我们在 2020 上半年开启了 LogicFlow 的项⽬,⽀持各系统的流程可视化需求。
LogicFlow 的能⼒和特性
LogicFlow 当前已具备了哪些能⼒呢,我会分两部分来介绍:
快速搭建流程图编辑器
提供了⼀个流程图编辑所必需的各项能⼒,这也是 LogicFlow 的基础能⼒:
•图的绘制能⼒。基于 SVG 来绘制形状各异的节点和线,并提供了基础的节点(矩形、圆形、多边形等)和线(直线、折线、曲线);
•图的交互。根据节点、线、图的各类⿏标事件(hover、点击、拖拽等)做出反应。⽐如节点拖拽、拖拽创建连线、线的调整、双击节点编辑⽂本等;
•提升编辑效率的能⼒。提供⽹格、对齐线,上⼀步、下⼀步,键盘快捷键,图放⼤缩⼩等配套能⼒,帮助⽤户提升编辑效率;
•提供了丰富的 API 。宿主研发通过 API 传参调⽤和监听事件的⽅式,与 LogicFlow 完成交互。
通过以上能⼒,前端研发可以低成本、快速的搭建起流程可视化的应⽤,提供流畅的产品交互。下⾯是通过 LogicFlow 内置的节点和配套能⼒,做的流程图⽰例:
基于业务场景拓展
设置图上所有元素的样式。⽐如各种节点、线、锚点、箭头、对齐线的⼤⼩颜⾊等,满⾜对前端样式调整的需求;•API 拓展。⽀持在 LogicFlow 上注册⾃定义的⽅法,⽐如通过 API 拓展提供图⽚下载的⽅法;
•⾃定义节点、线。内置的矩形、圆形等图形类节点往往⽆法满⾜实际的业务需求,需要定义具有业务意义的节点。
LogicFlow 提供了的⽅式让⽤户定制具有⾃定义图形、业务数据的节点,⽐如流程审批场景中的 “审批” 节点;•拓展组件。LogicFlow 在 SVG 图层上提供了 HTML 层和⼀系列坐标转换逻辑,并⽀持在 HTML 层注册组件。宿主研发可以通过 LogicFlow 的 API,基于任何 View 框架开发组件,⽐如节点的右键菜单、控制⾯板等;
•数据转换 adapter。LogicFlow 默认导出的图数据不⼀定适合所有业务,此时可以通过 adapter API,在图数据从LogicFlow 输⼊、输出的时候做⾃定义转换,⽐如转换成 BPMN 规范的图数据;
基于上述拓展的能⼒,前端研发能够根据实际业务场景的需求,灵活的开发出所需的节点、组件等。
下⾯有两个基于LogicFlow 拓展能⼒做出的流程图:
BPMN
BPMN
审批流程定位对⽐
上图是通过横纵两个维度来对⽐⽬前⼤家⽿熟能详的⼏个开源框架,以了解 LogicFlow 的定位。横轴是该框架在图可视化能⼒的丰富程度,纵轴越靠上则代表这个框架在业务流程应⽤上的成熟度越⾼,初次部署的开发成本越低。让我们分别来介绍⼀下这⼏个框架:
•activiti 作为⼯作流引擎提供了前后端的解决⽅案,简单⼆次开发就可以部署⼀套业务流程的管理平台;•Bpmn.js:基于 BPMN2.0 规范,设计的流程图编辑器;
•G6:antv 旗下专注图形可视化,各类分析类图表。⽐如⽣态树、脑图、辐射图、缩进图等等;
•X6:图编辑引擎,核⼼能⼒是节点、连线和画布。不仅⽀持了流程图,还有 Dag 图、ER 图。
LogicFlow 的定位在上图的 Bpmn.js 和 X6 之间,填补中间的空⽩。核⼼提供了流程图的编辑器,并且通过拓展能⼒来⽀持 BPMN 等规范所需的流程节点和数据格式,以满⾜当前业务下的现状。
实现原理和架构整体架构图
核⼼包 @logicflow/core 提供了流程图编辑器基础的能⼒,右边的 @logicflow/extension 是基于 @logicflow/core 的拓展性开发的插件。
流程图编辑器的设计⽅案
主要介绍⼀下实现流程图编辑器重要的选型和⽅案设计:
图渲染⽅案
前端绘制图形⽆⾮就是 HTML + CSS、Canvas、Svg 三种⽅式,我们综合做了⼀下对⽐,列出了相应的优劣势:
前端绘制图形⽆⾮就是 HTML + CSS、Canvas、Svg 三种⽅式,我们综合做了⼀下对⽐,列出了相应的优劣势:
在流程图的场景下,不需要渲染⼤量的节点(最多⼏千个元素),对于动画的诉求也不⾼。Svg 基于 DOM 的特性会更适合我们,⼀个是学习成本和开发成本更低,另⼀个是基于 DOM 可以做的拓展也更多。不过 Svg 标签内部并不⽀持插⼊其他⽐如 div 这种标签,所以在实现某些功能的时候,都需要结合其他 HTML 标签。
所以最终我们选择使⽤ HTML + Svg 来完成图的渲染,Svg 负责图形、线的部分,HTML 来实现⽂本、菜单、背景等图层。
模块抽象
基于上述⽅案,下⼀步我们要做的是对实现⼀张流程图做分类和抽象。
通过上图:
我们构建了多个图层来承担不同的职责,以⽅便实现功能和能⼒拓展。最上层的是 Svg 图层,所有图形(节点、线、对齐线、outLine 等)均在 Svg 上渲染,也负责监听图上的各种事件。Svg 下层的分别是组件层,负责拓展 UI 组件;Grid 层,负责渲染⽹格;背景层,添加⾃定义的背景;
Shape 的职责主要是基于 Svg 对图形渲染的封装,提供默认样式、把⽤户传⼊的属性做转换等,主要包含 Rect、Circle、Ellipse、Polygon、Path、PolyLine、Text 等,⽅便 LogicFlow 内部复⽤,⽐如圆形节点和锚点都需要Circle;
基于 Shape,还实现了很多⼩元素,⽐如节点和线需要的锚点,⽐如线上的箭头等等;
⽽ BaseNode、BaseEdge 则是节点和线通⽤能⼒的封装,聚合 shape、锚点、⽂本,还封装了对事件和样式的处理等。通过继承 BaseNode,传⼊ shape 我们可以得到 RectNode、CircleNode 等可渲染的节点。
MVVM + Virtual DOM
⾸先我们考虑到整个图编辑器具备很多状态存储,并且要实现编辑图上各模块的响应就必须要有状态的通信能⼒。第⼆如果要实现类似 redo/undo 这类功能,那整个图就⼀定需要根据数据得出渲染,即 f
n(state) => View ,⽐较好的⽅式就是通过 Model 来驱动 View。
最终我们选择基于 MVVM,这个⼴泛被应⽤于当前前端⼯程中的设计模式来构建 LogicFlow 的图编辑器,定义图的View 和 Model 层,使⼯程代码具备⼀定的解耦。与此同时,引⼊ Mobx 来实现我们的状态管理、数据响应的能⼒,⼀张图基于⼀份 Model 做状态的通信。此外,考虑 Mobx 的另⼀个原因是:只要我想,那就可以做到最细颗粒度的数据绑定(观测),可以减少没必要的渲染。
以下是 LogicFlow 图编辑器的 MVVM ⽰意图:
通过上图可以看到,View 层(图、节点等)通过数据绑定,会在 Model 发⽣变化之后做出响应/更新。前⾯我们提到了关于图的渲染我们是基于 Svg + HTML 实现的,那要做 View 层的更新⽆⾮就是命令式和声明式两个选择:
命令式。⽐如 jQuery 的 api,$('.rectNode').attrs({x: 1, y: 2}),像这种⽅式操作 DOM 代码其实⽐较繁琐,在重交互的场景下写的代码会⽐较冗余。虽然我们最终到了有⼀个库能够很⽅便的⽀持通过命令式的⽅式来绘图 —— antv/g。
声明式。⽐如 React/Vue 这类 View 框架,其中⼀个⽐较核⼼的能⼒就是做到了 state => UI ,通过声明式的⽅式来构建DOM,只要状态发⽣变化,那 UI 就更新。
除了考虑到命令式在操作 DOM 的场景下写代码会⽐较繁琐之外,还有⼀个原因就是操作 DOM 的成本问题,在基于State 更新 UI 的设计下,我们⾃然⽽然想到了引⼊ Virtual DOM 来解决某些场景下的更新效率,这也可以⼀定程度上弥补「基于 Svg 渲染图形」可能造成的渲染性能问题。
总之,选择 MVVM 的设计模式并引⼊ Virtual DOM,最根本的两个原因是:
•提升我们图编辑器场景下的开发效率。
•以及在 HTML + Svg 的图渲染⽅案下,可以追求更好的性能表现。
我们与 X6 做了⼀次渲染时的性能⽐较,在相同的运⾏环境下,分别测出 LogicFlow 和 X6 在不同量级的节点/线下,渲染出流程图的时间,理论上渲染时间越短,性能表现约好。
通过上述表格,我们测算出 LogicFlow 在初始渲染速度上是优于 X6 的,并且这还没有开启LogicFlow 的按需加载功能,也验证了我们的技术选型。你也可以在⽰例页进⾏测试:
事件系统
介绍了在 “状态” 和 “响应” 我们做的设计,那要收集到⽤户的各类 “操作” 并及时上报和冒泡,就需要⼀套事件系统。最主要的就是复⽤和统⼀上报。jquery框架定义
复⽤即怎么保证所有节点和线都能具备默认的事件回调,以及针对复杂事件(拖拽)的处理逻辑如何共⽤。
复⽤即怎么保证所有节点和线都能具备默认的事件回调,以及针对复杂事件(拖拽)的处理逻辑如何共⽤。
•Behavior。针对复杂事件的处理,我们做了 function 和 class 形式的封装,⽐如 Drag 是通过 mousemove、down、up 来模拟 h5 的 dragEnter、dragOver、dragEnd 和 drop 事件,DnD 则是通过抽象 dragsource 和 droptarget 两个实体来实现 drag 和 drop 的交互,⽐如拖拽创建节点;
•在前⽂模块抽象章节提到了内部有 BaseNode 和 BaseEdge 这样的抽象,内置节点和⾃定义的节点都通过继承基类来获得通⽤的能⼒,所以 LogicFlow 内部默认的事件回调实际是通过继承来复⽤的;
•EventCenter。通过事件总线做统⼀上报,把内部捕获到的所有⽤户⾏为事件,按照⼀定的规范和格式 emit(ev, args)都上报到 EventCenter,最终冒泡到 LogicFlow 类,由 LogicFlow 类统⼀跟宿主交互。此外,图编辑器内任何地⽅也都可以通过 EventCenter 做事件的触发和监听。
⼯具中⼼
⼯具中⼼的定位是解决某类特定问题的 utils,⽐如上⾯提到的 Behavior(复杂事件的封装)和 Event
Center。此外,在图编辑的过程中,如果要实现⽐较好的交互效果,实际有很多复杂的计算逻辑要处理。
坐标系。浏览器的 clientX、clientY 坐标系,以及 Svg 图本⾝的坐标系,当出现图的缩放和平移的时候,两个坐标系是不同的,在事件处理的时候就需要做两个坐标系的转换。
Algorithm。是专门通过⼏何、算法来处理可视化的⼀些问题。⽐如:当⼀个节点在同⼀⽅向有多条折线连出的时候,如何做路径的合并以展⽰起来更美观,如下:
如何计算出⼀根线到⼀个图形的切点,以达到线可以连接图形⾮锚点的位置,如下图:
History。主要提供 redo 和 undo 的能⼒。通过两个栈来存储 undos 和 redos,并限制最⼤长度,得益于 MVVM 的设计模式,能⽅便的做数据变化的观测和 Model 驱动 View。
可扩展性
介绍完流程图编辑器的设计⽅案,现在来介绍 LogicFlow 的另⼀个重要特性,关于拓展性⽅⾯的设计。对于LogicFlow,是解决某个领域问题的开发框架,⾸先 API 要具备可扩展性;此外 LogicFlow 还提供了视图层,在 View 部分应该能够让⽤户做⼆次开发。这两个扩展的⽅向确定之后,最主要的还是结合业务需求,要能满⾜当前和未来⼀段时间内预见的业务场景,但也不能过度设计。
API 上的设计
⾸先,LogicFlow 在⾯向⽤户使⽤这⼀层,完全是基于⾯向对象的设计模式封装的,最⼤的好处是⼏乎每个程序员都熟悉它的使⽤,使⽤成本低。通过下⾯初始化⽅式便可以了解。
通过 class LogicFlow,⽤户实例化⼀次便得到⼀个流程图的实例,状态也是私有的,各种使⽤⽅法通过 lf 的实例调⽤

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。