前言自从 LogicFlow 正式开源后,受到的关注比我们想象的要多。在最开始打算要做 LogicFlow 的时候,我们花了很多的精力去讨论要做一个什么样的流程可视化库。其中一个选择是基于现有的业务直接实现一个开箱即用,包括了所有的流程编辑库常用功能的库。但是最终还是没有选择这样做,因为我们当时的背景是:不同的项目中,在流程图的外观、后台所需数据格式都存在较大的差异。有些项目用的是 activity, 有些是某些团队自研的流程引擎。所以我们需要做一个能支持各系统平滑迁移的流程可视化库,需要这个库足够的灵活,视觉上也要满足个各系统自己的风格,而且最好是流程图上的各种功能都可以自按需使用。 基于插件化的拓展机制配置化 or 插件化?我们对于如何将 LogicFlow 实现成一个足够灵活的流程可视化库存在两种想法。一种是一切皆可配置,也就是配置化。这个是很多可视化库的做法,最典型的就是 ECharts,用过 ECharts 的人应该都知道,它的配置功能特别丰富,几乎可以做到配置任何图上的元素的效果,从而达到业务开发者需要的自定义效果。 但是对于我们来说,配置化为了保持其提供足够的灵活性,我们需要在内部维护太多的 UI 相关的逻辑。以一个节点为例: 如果是采用配置化的方式的话,开发者传入的配置大致是: javascript复制代码{ type: 'rect', icon: 'https:///settings.png', text: '22\n33333',} 我们就需要在内部代码中判断传入的参数是否有 icon,如果有,就在左上角显示 icon。但是有的流程希望自己在左上角显示的是文本,我们可能就要再增加一个字段 nodeText 来控制了。那如果我们想显示一个图标在右边呢?想里面显示 3 列文本呢? 总的来说,配置化虽然对业务开发者足够友好,只需要文档足够完善,库内部兼容的效果足够多,就可以达到很好的效果。但是对于我们来说却是需要付出很大的成本去实现各种效果,而且很可能因为一些其他情况,舍弃支持一些不常用的效果。从长远来看,这种配置化方案对我们来说还是不够灵活的。 另一种方案就是插件化,就是将不断变化的非核心功能分散到插件中,避免其与核心代码耦合,保持其核心部分代码简洁和稳定。在插件化方案下,我们就可以实现一个支持设置 icon 节点就好了,至于其它特殊需求的节点,就有用户自己利用自定义机制来实现即可。而且开放到社区后,其它开发者自定义的内容也可以当做一个插件,贡献到 LogicFlow 中来。 插件化会带来很多好处:
API 健全性&稳定性什么是插件?插件是在核心程序上遵循其提供出来的接口规范再次编写的出来的程序。插件是不能脱离核心程序单独存在的,是一种对核心程序功能的扩展。所以个人觉得插件化最重要的一点就是核心程序的 API 健全性和稳定性。如果我们的 API 不够完善,社区开发者是无法开发出符合其需求的插件;如果我们的 API 变化很大,很有可能导致之前开发的插件在我们版本升级后就不能用了,会导致整个 LogicFlow 的社区生态混乱。 为了保证 API 的健全性和稳定性,我们做了这些事情:
内置插件如果我们只提供表示这图编辑部分的
LogicFlow 本身只是一个单纯的流程图编辑器,不带有业务属性。为了更好的易用性,我们提供了 Bpmn-js 插件,让使用 bpmn-js 的项目能够快速替换。有了 Bpmn 插件后,直接通过 LogicFlow 装载 bpmn 插件,这个页面就表现成为 bpmn-js 了。 javascript复制代码import LogicFlow from '@logicflow/core';import { Bpmn } from '@logicflow/extension';LogicFlow.use(Bpmn); LogicFlow 的拓展能力前面提到过,插件的扩展性是否强大,是看核心程序提供的 API 是否有足够的扩展性。LogicFlow 在绝大多数 API 上的设计,其目标就是支持的拓展能力。我们在 自定义节点为了提高易用性,在节点方面,LogicFlow 内置了基础节点,然后在
自定义节点规则在某些时候,我们可能需要控制连线的连接方式,比如 A 节点不能作为连线的起点、B 节点不能作为连线的终点、C 节点后面必须是 A 节点等等。LogicFlow 提供了自定义节点规则功能来实现这个需求。 LogicFlow 内部有 javascript复制代码class CnodeModel extends RectModel { /* ignore other code*/ // 判断这个节点的下一个节点是否符合自定义要求 getConnectedSourceRules(): ConnectRule[] { const rules = super.getConnectedSourceRules(); const gateWayOnlyAsTarget = { message: 'C节点下一个节点只能是A节点', validate: (source: BaseNode, target: BaseNode) => { let isValid = true; if (target.type !== 'a-node ') { isValid = false; } return isValid; }, }; rules.push(gateWayOnlyAsTarget); return rules; } // 判断这个节点的上一个节点是否符合自定义要求 getConnectedTargetRules() {}} 自定义连线自定义连线方案和自定义节点基本一致,由基础连线实现线的绝大部分逻辑,然后在内置连线中实现连线的特殊交互处理,最后再由开发者基于内置连线进行自定义开发。当然,由于绝大多数图编辑上线的表现形式都只有直线、折线和曲线三种形式,所以一般开发者自定义连线都是改变一下样式(颜色、虚线)和名字(如Bpmn中连线叫做 自定义属性一般来说,对于一个节点,我们只需要 LogicFlow 中提供了一个 javascript复制代码lf.register('custom-process', ({TriangleNode,TriangleModel }) => { class CustomProcessNode extends TriangleNode { static extendKey = 'CustomProcessNode'; getShapeStyle() { const attributes = super.getShapeStyle(); const properties = super.getProperties(); // 判断自定义属性customStatus是否为error, // 如果是,则将这个节点的填充颜色设置为红色。 if (properties.customStatus === 'error') { attributes.fill = 'red' } return attributes; } } return { view: CustomProcessNode, model: PolygonNodeModel, };}); 上面的代码自定义了一个 custom-process 节点,当传入的数据中节点 type 为 custom-process,节点属性中 customStatus 为 error 的时候,流程图上显示的就是一个红色的三角形。 自定义组件在 LogicFlow 中,除了节点和连线这种由 svg 渲染的图形外,还存在着一些用于图编辑过程中进行控制的组件,这些组件 LogicFlow 是通过 html 来实现的(比如菜单、控制面板等)。LogicFlow 开放了在图上插入 DOM 的能力,开发者就可以基于这个能力来实现自定义组件。 有了在图上自由插件 DOM 的能力,我们就可以做很多事情了,比如可以实现一个自由调整节点颜色、字体大小的工具。这个工具的开发就只需要按照我们正常前端开发即可。然后在监听到用户选中节点后,将这个 DOM 插入到节点对应的位置旁边。 主题上面我们提到过可以用自定义节点来定制任何节点的外观,但是对每个节点都单独自定义一次太多麻烦。LogicFlow 提供了主题功能,来统一设置所以节点的外观基础属性。比如我们想让所有的矩形都不带边框。 javascript复制代码lf.setTheme({ rect: { strokeWidth: 0 },}) 主题除了可以设置节点和连线的外观以外,还可以设置内部功能的样式,比如文本、对齐线等。 最后通过上面的介绍,大家对 LogicFlow 的拓展机制应该有了一定的了解。LogicFlow 本身不是一个专门处理某个场景的流程设计工具,而是一个流程图编辑库。大多数情况下,拓展性强就意味着无法开箱即用,为了让 LogicFlow 成为一个开箱即用的库,LogicFlow 采用了插件化的机制,通过插件,将场景限定到实际业务场景中来。 LogicFlow 还是一个很新的开源项目,提供的插件还不够丰富,也存在业务场景考虑不周的情况,欢迎大家在 github 上提 issue,我们一定会认真对待每一个 issue!LogicFlow 也在寻找 contributor ,如果你感兴趣的话,欢迎一起来共建! |
|