ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

LogicFlow 架构设计解析:一个面向业务自定义的流程图编辑框架是如何炼成的

2026/9/15 12:26:00 拓冰建站 浏览量
LogicFlow 架构设计解析:一个面向业务自定义的流程图编辑框架是如何炼成的 LogicFlow 架构设计解析一个面向业务自定义的流程图编辑框架是如何炼成的【免费下载链接】LogicFlowA flow chart editing framework focus on business customization. 专注于业务自定义的流程图编辑框架支持实现脑图、ER图、UML、工作流等各种图编辑场景。项目地址: https://gitcode.com/GitHub_Trending/lo/LogicFlow导读本文以 LogicFlow 官方《设计和架构》文档为骨架结合当前仓库源码packages/core、packages/extension等逐层拆解这个由滴滴体验平台技术团队自研的流程图编辑框架为什么放着 BPMN.js、X6 不用而要自研为什么用「HTML SVG」而不是 Canvas 渲染MVVM Virtual DOM 与 MobX 状态管理如何驱动「数据到视图」事件系统、工具中心、API 与插件化又如何支撑业务的深度自定义。读完本文你将获得一套完整的「自研流程图编辑器」设计方法论并能在实际业务中正确使用 LogicFlow 的初始化、注册、事件、adapter 与插件机制。LogicFlow 简介面向业务定制的流程可视化框架LogicFlow 是滴滴体验平台技术研发的一款流程图编辑框架它提供了一系列流程图交互、编辑所必需的功能以及简单灵活的节点自定义、插件等拓展机制让业务系统可以低成本地满足类流程图的需求。目前LogicFlow 已经在客服业务下的 IVR互动式语音应答、工单流转、智能机器人等多个运营系统中落地验证。从当前仓库的工程结构可以清晰看到其产品形态packages/core核心包logicflow/core提供流程图编辑器基础能力渲染、交互、编辑、事件、历史等packages/extension扩展包logicflow/extension基于 core 的可拓展性开发沉淀了 BPMN 节点与 adapter、泳道pool、动态分组dynamic-group、标签label、控制面板control、右键菜单menu、迷你地图mini-map等通用能力且支持单独安装、按需引入packages/engine配套的流程执行引擎对应文档中提到的流程引擎规划。LogicFlow 的核心定位是快速搭建流程编辑应用 基于业务场景灵活拓展。为何自研面向多样化业务的现实选择文档中给出了自研的两个根本原因这决定了 LogicFlow 后续所有架构决策的方向业务诉求多样且变更快客服系统要支持几乎所有事业部的诉求流程配置需求千人千面。传统面向场景的编程成本高、周期长因此需要线上配置化的运营系统让运营、产品同学通过画流程图的方式变更线上业务逻辑如电话进线时的互动式语音应答、人工客服的标准作业流程等。市面框架存在明显短板调研了 BPMN.js、X6、Jsplumb、G6-editor 等框架后发现BPMN.js、Jsplumb 的拓展能力不足自定义节点支持成本很高只能全量引入各系统无法按需引入与后端配套的流程引擎适配成本较高均不支持数据转换、不支持流程校验等业务定制需求。于是团队在 2020 上半年启动了 LogicFlow 项目。从「为何自研」可以提炼出一条贯穿全文的设计主线拓展性可扩展性是 LogicFlow 的第一公民后续的图层设计、MVVM 选型、事件系统、API 设计、插件化都是围绕这条主线展开的。LogicFlow 的能力与特性快速搭建流程图编辑器LogicFlow 的基础能力覆盖了一张流程图编辑器从「画出来」到「用起来」的完整链路图的绘制能力基于 SVG 绘制形状各异的节点和线内置矩形、圆形、多边形等基础节点以及直线line、折线polyline、曲线bezier三种基础边。交互能力根据节点、线、图的各类鼠标事件hover、点击、拖拽等做出反应比如节点拖拽、拖拽创建边、线的调整、双击节点编辑文本等。编辑提效能力提供网格Grid、对齐线Snapline、上一步/下一步History、键盘快捷键Keyboard、图放大缩小Zoom等配套能力。丰富 API宿主研发通过 API 传参调用和监听事件的方式与 LogicFlow 完成交互。这些能力在源码中均有对应落点例如内置节点的注册集中在 packages/core/src/LogicFlow.tsx 的defaultRegister()方法第 302–359 行默认注册了rect、circle、polygon、text、ellipse、diamond、html七种节点以及line、polyline、bezier三种边全部通过统一的「view model」注册机制完成。基于业务场景拓展当基础能力无法满足业务需求时LogicFlow 提供了一套完整的分层拓展手段样式定制设置图上所有元素的样式包括节点、线、锚点、箭头、对齐线的颜色大小等对应setTheme/getTheme及主题系统见 packages/core/src/LogicFlow.tsx 中Theme类型定义涵盖baseNode、rect、polyline、anchor、arrow、snapline等全套主题项。API 拓展支持在 LogicFlow 上注册自定义方法例如通过 API 拓展提供图片下载能力。自定义节点、线通过继承BaseNode/BaseEdge或RectNode/RectModel等内置类定义具有业务意义的节点例如流程审批场景中的「审批」节点。拓展组件LogicFlow 在 SVG 图层之上提供了 HTML 层和一系列坐标转换逻辑支持在 HTML 层注册组件。宿主研发可以基于任何 View 框架开发组件比如节点的右键菜单、控制面板等。数据转换 adapter通过adapterIn/adapterOut在图数据输入、输出时做自定义转换例如转换成 BPMN 规范的图数据。内置拓展包 extension单独安装、按需引入沉淀客服业务下具有通用性的节点、组件等例如 BPMN 规范节点与数据 adapter、默认菜单等。其入口统一在 packages/extension/src/index.ts当前已包含 BPMN、bpmn-adapter、bpmn-elements、turbo-adapter、pool泳道、dynamic-group动态分组、insert-node-in-polyline、snapshot截图、auto-layout自动布局、control、menu、mini-map 等能力。仓库内提供了多个可直接运行的示例工程可用于验证上述能力BPMN 相关的页面见 examples/feature-examples/src/pages/extensions/bpmn动态分组相关示例见 examples/dynamic-group-regression。实现原理与架构整体架构core 与 extension 的分层设计LogicFlow 的整体架构遵循「核心包 扩展包」的经典分层核心包logicflow/core提供流程图编辑器基础能力右边的logicflow/extension是基于 core 的拓展性开发的插件集合。下面这张架构图来自当前仓库 sites/docs/public/logicflow-8-7.jpg直观展示了从「对外 API → 核心模块View 视图层 / Model 模型层→ 工具类方法 → 底层依赖」的分层以及右侧基于核心包衍生的 extension 生态从图中可以看出核心模块被清晰地划分为两大块View 视图层自定义元素Custom Elements、形状ShapeRect、Circle 等、图GraphOutline、Bezier Adjust 等Model 模型层自定义模型CustomModel、基础节点/边模型BaseNodeModel/BaseEdgeModel、吸附线模型SnaplineModel、编辑配置模型EditConfigModel、变换模型TransformModel、图模型GraphModel。View 与 Model 之间通过 Actions事件/操作、Observable State状态观察、Computed Values计算值连接这正是后文「MVVM Virtual DOM」设计的直观体现。图渲染方案为什么选 HTML SVG 而不是 Canvas前端绘制图形无非三种方式HTML CSS、Canvas、SVG。LogicFlow 团队对其优劣做了综合对比方案主要优势主要劣势LogicFlow 的取舍HTML CSS上手简单、生态丰富不适合复杂图形绘制与精确控制用于文本、菜单、背景等图层Canvas性能强、适合大量元素与复杂动画开发成本高、事件与 DOM 集成困难流程图中节点数量有限最多几千个动画诉求不高未采用SVG基于 DOM、学习与开发成本低、可拓展性强大规模元素渲染性能弱于 Canvas核心图形渲染方案在流程图场景下不需要渲染大量的节点最多几千个元素对动画的诉求也不高。SVG 基于 DOM 的特性更合适一是学习成本和开发成本更低二是基于 DOM 可以做的拓展更多。不过 SVG 标签内部不支持插入div等 HTML 标签所以在实现某些功能时需要结合其他 HTML 标签。因此最终的选择是使用 HTML SVG 完成图的渲染SVG 负责图形、线的部分HTML 实现文本、菜单、背景等图层。这一「双层渲染」设计在 packages/core/src/view/Graph.tsx 中有非常直观的体现——渲染一棵组件树时同时输出多个图层CanvasOverlaySVG 元素层所有节点、边、fakeNode 都在此渲染svg根节点见 packages/core/src/view/overlay/CanvasOverlay.tsx同时负责滚轮缩放、拖拽平移、双指捏合、空白处点击/右键等画布级事件ModificationOverlay虚线外框层包含选择 Outline、Bezier 调整点、对齐线Snapline等ToolOverlay工具层承载插件注册的 UI 组件BackgroundOverlay画布背景层Grid网格层点状dot/ 交叉线mesh两种类型见 packages/core/src/view/overlay/Grid.tsx。这种「一个画布、多个职责明确的图层」的设计为后续的功能扩展菜单、控制面板、对齐线、框选等提供了天然的挂载位置。模块抽象从 Shape 到 BaseNode/BaseEdge在确定渲染方案后下一步是对一张流程图做分类和抽象核心抽象层级如下图层Overlay多个图层承担不同职责最上层是 SVG 图层所有图形节点、线、对齐线、outline 等都在 SVG 上渲染并负责监听图上的各种事件SVG 下层分别是组件层拓展 UI 组件、Grid 层渲染网格、背景层自定义背景。Shape基于 SVG 对图形渲染的封装提供默认样式、把用户传入的属性做转换等。主要包含 Rect、Circle、Ellipse、Polygon、Path、PolyLine、Text 等方便 LogicFlow 内部复用——比如圆形节点和锚点都需要 Circle。小元素基于 Shape 实现的小元素例如节点和线需要的锚点Anchor、线上的箭头Arrow。BaseNode / BaseEdge节点和线通用能力的封装聚合 shape、锚点、文本并封装对事件和样式的处理。通过继承BaseNode、传入 shape 可以得到RectNode、CircleNode等可渲染的节点。由于流程图是富交互重编辑场景有了这些基础模块之后接下来要解决的是「富交互的方案设计」——用户在图上做的任何操作都要给出响应触发一个节点的拖拽关联的线需要跟着动拖拽过程中要能识别水平线上有没有其他节点对齐线。这一抽象的直接受益者是自定义能力自定义节点只需继承RectNode/RectModel等内置类即可获得完整的通用能力。register方法的注册参数中也显式暴露了全部内置基类见 packages/core/src/LogicFlow.tsxBaseNode、BaseNodeModel、RectNode、RectNodeModel、CircleNode、PolygonNode、LineEdge、PolylineEdge、BezierEdge等。MVVM Virtual DOM数据驱动的视图更新LogicFlow 选择 MVVMModel-View-ViewModel设计模式构建图编辑器理由有二整个图编辑器具备很多状态存储要实现编辑图上各模块的响应必须要有状态通信能力要实现 redo/undo 这类功能整个图必须能「根据数据得出渲染」即fn(state) View比较好的方式是通过 Model 驱动 View。具体落地方式是引入MobX做状态管理、数据响应一张图基于一份 Model 做状态的通信同时MobX 支持最细颗粒度的数据绑定观测可以减少不必要的渲染。View 层图、节点等通过数据绑定会在 Model 发生变化之后做出响应/更新。在做 View 层更新时有命令式和声明式两个选择命令式如 jQuery 的$(.rectNode).attrs({x: 1, y: 2})操作 DOM 代码比较繁琐在重交互场景下写出的代码会比较冗余声明式如 React/Vue 这类 View 框架核心能力是state UI只要状态发生变化 UI 就更新。除了命令式代码繁琐之外还有一个原因是操作 DOM 的成本问题。在基于 State 更新 UI 的设计下引入Virtual DOM可以解决某些场景下的更新效率问题这也在一定程度上弥补了「基于 SVG 渲染图形」可能造成的渲染性能问题。从源码看LogicFlow 的 View 层基于Preact兼容 React 语法的轻量虚拟 DOM 实现 MobX构建packages/core/src/index.ts中import { observer as mobxObserver } from mobx-preact所有节点 View 组件通过observer包装以获得响应式更新能力注册自定义节点时若未显式关闭isObserverView会默认用observer()包装见 packages/core/src/LogicFlow.tsx。Graph组件本身也是observer装饰的类组件。选择 MVVM Virtual DOM最根本的两个原因便是提升图编辑器场景下的开发效率以及在 HTML SVG 的图渲染方案下追求更好的性能表现。事件系统复用、统一上报与事件总线要收集用户的各类「操作」并及时上报和冒泡就需要一套事件系统其核心是复用和统一上报。Behavior复杂事件封装针对复杂事件的处理做 function 和 class 形式的封装。例如Drag通过 mousemove、down、up 来模拟 HTML5 的 dragEnter、dragOver、dragEnd 和 drop 事件DnD通过抽象 dragsource 和 droptarget 两个实体来实现 drag 和 drop 交互如拖拽创建节点。相关实现见 packages/core/src/view/behavior。继承复用默认事件前文提到的BaseNode、BaseEdge抽象内置节点和自定义节点都通过继承基类获得通用能力因此 LogicFlow 内部默认的事件回调实际上是通过继承来复用的。EventCenter事件总线通过事件总线做统一上报把内部捕获到的所有用户行为事件按照一定规范和格式emit(ev, args)上报到 EventCenter最终冒泡到LogicFlow类由LogicFlow类统一与宿主交互。此外图编辑器内部任何地方也都可以通过 EventCenter 触发和监听事件。EventCenter 的实现位于 packages/core/src/event/eventEmitter.ts支持on/once/off/emit并且有两个值得注意的细节事件名支持逗号分隔的批量监听/触发如lf.on(node:click,node:contextmenu, cb)支持*通配符监听所有事件。LogicFlow实例对外暴露了与 EventCenter 一一对应的on/off/once/emit四个方法见 packages/core/src/LogicFlow.tsx宿主研发通过观察者模式订阅内部各类事件这是 API 设计中「观察者模式做通信」的落点。工具中心坐标系、Algorithm 与 History工具中心的定位是解决某类特定问题的 utils比如前面提到的 Behavior 和 EventCenter。在图编辑过程中要实现比较好的交互效果实际有很多复杂的计算逻辑要处理坐标系浏览器的 clientX/clientY 坐标系与 SVG 图本身的坐标系在图的缩放和平移时是两个不同的坐标系需要做坐标系转换。对应 API 如getPointByClient返回 HTML 层坐标domOverlayPosition与 SVG 层坐标canvasOverlayPosition内部实现在 packages/core/src/LogicFlow.tsx。Algorithm通过几何、算法处理可视化问题例如当一个节点在同一方向有多条折线连出时做路径的合并以展示更美观计算一根线到图形的切点使线可以连接图形非锚点的位置。相关代码集中在 packages/core/src/algorithm例如getVerticalPointOfLine通过三角函数计算箭头位置与边的点击区域edge.ts、outline.ts处理折线合并与轮廓计算。History提供 redo/undo 能力。通过两个栈存储 undos 和 redos并限制最大长度得益于 MVVM 设计模式能方便地做数据变化的观测和 Model 驱动 View。具体实现见 packages/core/src/history/index.tsundos与redos两个栈、maxSize 50超过后 shift 掉最旧记录、waitTime 100ms通过debounce合并短时间内的连续变更并通过mobx-utils的deepObserve监听 Model 变化自动入栈。可扩展性LogicFlow 的架构灵魂介绍完编辑器设计再来介绍 LogicFlow 另一个重要特性——拓展性设计。对于 LogicFlow 这个解决领域问题的开发框架首先 API 要具备可扩展性此外 LogicFlow 提供了视图层View 部分应该能够让用户做二次开发。这两个方向确定之后最主要的是结合业务需求满足当前和未来一段时间内可预见的业务场景但不能过度设计。API 上的设计面向对象 方法拓展 观察者 数据可定制LogicFlow 在面向用户使用这一层完全基于面向对象的设计模式封装最大的好处是几乎所有程序员都熟悉它的使用使用成本低。通过下面的初始化方式便可以了解const lf new LogicFlow({ // 实例化 lf 对象 container: document.querySelector(#graph), // 获取渲染容器 width: 700, height: 600, background: { color: #F0F0F0 }, grid: { type: dot, size: 20, }, }); lf.render({ nodes: [], edges: []}); // 在界面上渲染视图通过class LogicFlow用户实例化一次便得到一个流程图实例状态也是私有的各种使用方法通过lf实例调用即可。结合 packages/core/src/options.ts 中的类型定义与默认值可将初始化选项进一步展开选项说明默认值来自Options.defaultscontainer渲染容器必填缺失会抛错无width/height画布宽高不传时默认父容器宽高SSR 场景如 Next.js/Nuxt.js 需显式设置无background背景配置color、backgroundImage、backgroundRepeat等传false关闭falsegrid网格配置支持number作为 size、boolean作为 visible或对象type: dot \| mesh、size、visible、config.color、config.thickness、majorBoldfalsekeyboard快捷键配置无默认启用内置快捷键style主题配置对应Theme结构无edgeType默认边类型line/polyline/bezier无isSilentMode静默只读模式节点和边不可移动、不可改文案、无锚点无stopScrollGraph/stopZoomGraph/stopMoveGraph禁止滚动画布 / 禁止缩放 / 禁止平移支持 boolean 或vertical | horizontal | [上,右,下,左]无snapline是否开启对齐线truetextEdit是否允许编辑文本trueoutline是否显示元素选中外框falsehistory是否开启历史记录undo/redo无默认开启plugins/pluginsOptions/disabledPlugins插件注册、插件配置、禁用指定插件无 / 无 /[]idGenerator/edgeGenerator自定义节点/边 id 生成器、自定义连线生成器无guards删除/克隆前拦截beforeDelete、beforeClone无API 拓展的设计总结为四点面向对象LogicFlow 内部做好封装用户可以继承、重写接口/方法方法设计方法有固定类型的输入输出同时提供类似extends的方法通过LogicFlow.use(fn)在原型上拓展方法观察者模式做通信提供on方法供宿主订阅各类内部事件图数据可定制无论是一个节点、线有哪些自定义业务属性properties还是流程图要导出什么样的数据都应该能够定制。数据定制的能力在源码中有明确体现LogicFlow实例上声明了adapterIn/adapterOut两个钩子见 packages/core/src/LogicFlow.tsx。lf.render(graphData)会先经过adapterIn转换再渲染第 1152–1158 行lf.getGraphData()会先取原始数据再经adapterOut输出第 1071–1077 行需要原始内部数据时可使用getGraphRawData()。仓库内packages/extension/src/bpmn-adapter与turbo-adapter就是这套机制的实际应用。插件化View 层二次开发的插拔能力View 层的拓展性除了用户能够定制展现方式之外最重要的是插件化流程可视化这条路上不同业务场景需要的能力不尽相同LogicFlow 很难支持所有场景所以提供良好的插拔能力、让用户二次开发是比较好的选择。目前在 UI 界面上开放了两个能力节点和线支持二次开发即自定义节点、线可开发 UI 组件注册到 LogicFlow 的组件画布内。基于插件化的思路LogicFlow 支持了不同业务系统并把一些通用的能力沉淀封装到lf-extension包中比如支持 BPMN 规范的节点。extension 内的拓展主要分为四类UI 组件、自定义节点、API、adapter。从 packages/core/src/LogicFlow.tsx 的插件机制看插件有两种形态installPlugin第 1411–1438 行类形态导出带pluginName的类实例化时注入{ lf, LogicFlow, props, options }render方法返回的组件会被推入components渲染到工具层ToolOverlay对象形态导出{ pluginName, install, render }install.call(extension, this, LogicFlow)完成安装。插件注册支持两种方式实例级plugins配置项以及全局LogicFlow.use(extension)第 1346–1363 行后者会存入静态extensionsMap所有 LogicFlow 实例都会使用两者同时存在时优先使用实例级插件并可通过disabledPlugins按名称禁用指定插件第 1382–1406 行。自定义节点的注册也提供了两种方式register方法第 169–273 行推荐使用对象方式import { RectNode, RectModel } from logicflow/core class CustomView extends RectNode { } class CustomModel extends RectModel { } lf.register({ type: custom, // 自定义类型名写入图数据中的 type 字段 view: CustomView, model: CustomModel })自定义 View/Model 注册后会自动被observer包装除非isObserverView: false从而获得 MobX 响应式更新能力。未来规划从流程可视化库到端到端方案原文档中 LogicFlow 团队对 v1.0 版本2021.4 发布的定义包括三个方面API 的易用性和丰富程度保持流程可视化的定位把 core 的 API 丰富、extension 的能力增强更完善的文档和示例文档易读、完善、提供英文版并有完整的示例和代码供开发者 copy-paste不仅是流程可视化库期望提供整套解决方案LogicFlow 只解决前端流程图编辑的技术问题关于图数据的定义、流程最终如何被执行还需要一个配套的流程引擎。从当前仓库的目录结构看这一规划已经在工程上得到印证packages/engine已成为独立的引擎包包含FlowModel、Scheduler、EventEmitter、recorder、condition 等模块并有04_execute、06_parallelExecution、07_interruptedAndResume等测试覆盖执行、并行、中断恢复等能力文档仓库中同时存在中文与英文双语架构文档见 sites/docs/docs/article/architecture-of-logicflow.en.md 的同级英文文件examples/feature-examples、examples/engine-browser-examples、examples/engine-node-examples等提供了可直接运行的示例工程。结语回顾全文LogicFlow 的架构设计可以浓缩为几条相互咬合的原则渲染选型HTML SVG 双层渲染SVG 管图形、HTML 管文本与 UI兼顾成本、拓展性与性能数据驱动MVVM MobX Virtual DOM用一份 Model 驱动整个图的状态通信与视图更新为 undo/redo、对齐线等重交互能力奠基统一事件Behavior 封装复杂交互、继承复用默认回调、EventCenter 统一上报冒泡宿主通过观察者模式订阅工具沉淀坐标系转换、几何算法、History 双栈等复杂逻辑下沉为工具中心保持业务代码清爽可扩展性优先面向对象 API、方法拓展、数据 adapter、插件化与自定义节点/边共同支撑了「快速搭建 业务定制」的双重目标。如果你的业务中也有流程编辑的诉求并且有较高的拓展性需求LogicFlow 的这套设计思路尤其是图层抽象与插件化本身就值得借鉴——你既可以直接在业务中引入它快速搭建编辑器也可以将其中的 MVVM 事件总线 插件化方法论迁移到自己的可视化产品中。【免费下载链接】LogicFlowA flow chart editing framework focus on business customization. 专注于业务自定义的流程图编辑框架支持实现脑图、ER图、UML、工作流等各种图编辑场景。项目地址: https://gitcode.com/GitHub_Trending/lo/LogicFlow创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考