Write By CS逍遥剑仙 我的主页: csxiaoyao.com GitHub: github.com/csxiaoyaojianxian Email: sunjianfeng@csxiaoyao.com
低代码浪潮下,市面上涌现出形形色色的可视化低代码平台,大体分为前后端两大类:
1. UI可视化: 主要解决组件页面布局及嵌套关系,用户通过拖拽组件、配置参数来调节功能和交互,最后发布并生成访问链接
2. 后端流程编排: 主要为面向业务侧的流程引擎,基于容器、微服务的后端流程编排,往往门槛高、功能较重
目前,UI 可视化工具在实际生产环境中使用存在瓶颈,没有技术背景的用户只能自助化生成逻辑简单的页面。举例来说,按钮组件虽然提供了各类丰富的功能供用户选择,如跳转链接、开关弹窗、执行请求、分享邀请等等,但现实的需求往往不是单一逻辑的操作,很可能是:执行抽奖请求,判断若抽到大奖,跳转用户信息收集页,若未中奖,则弹窗提醒。虽然大部分系统一般会提供功能扩展,并且支持复用,但是这样的长链路的逻辑千变万化,导致很难复用。
可以看出,UI可视化侧重于界面渲染,对于前端复杂交互逻辑支持的灵活性,尤其是长逻辑链的配置捉襟见肘;而对于后端逻辑编排,其产物往往以暴露接口的形式呈现,其逻辑编排针对的是后端服务,难以触达前端逻辑的编排。
对自助化工具来说,灵活性和低门槛是相矛盾的,如何在尽量不提高使用门槛的前提下满足不胜枚举的差异化需求?主流的 UI 可视化工具通过以下方式来实现功能逻辑的扩展:
基础方案 | 主流方案 | 【内部自研项目】 | 【内部自研项目】 + 逻辑编排 | |
---|---|---|---|---|
方式 | 自写代码引入 | [组件库] 封装自定义组件 | [组件库+事件库] (支持event绑定) 将 UI 和逻辑分离,组件绑定不同事件 | (支持action编排) 事件支持更细粒度的动作逻辑编排 |
优点 | 实现简单 | 支持复用,通过开发组件,用户配置组件支持的 props,能够满足大多数场景需要 | 业务逻辑开发门槛和成本大幅降低 | 长逻辑链的微小差异通过编排实现,趋近零代码 |
问题 | 对无技术背景用户不友好,代码复用性低,只适合逻辑简单的静态页 | 组件开发成本高,预置的逻辑代码固定封装在组件中,微小功能差异需要开发整个组件,造成冗余 | 用户需求千变万化,无法枚举,长逻辑链中的微小差异仍无法自助化实现,灵活性有提升空间 |
在 【内部自研项目】 项目两年多的实践案例中,通过 UI组件 + 事件绑定 的方式,可以轻松解决 90% 以上的常规需求。
具体参考下面这个案例,需要控制页面中的4个活动模块在活动开始前、进行中、结束后的不同状态,活动开始前后点击展示不同弹窗,活动期间点击跳转指定 url。
对于这样的需求,如下图所示,在 【内部自研项目】 中拖入 4 个 "热区按钮" 组件,分别绑定 "【通用】在开放时间内展示激活状态,点击跳转链接" 事件,并配置对应参数,即可轻松实现。
在现实的营销活动中,90% 的场景产品经理的确可以通过上述方式零代码实现,但若此时需求来个小变更:活动结束后跳转到活动总结页面,此时就变为了剩下的 10% 的低代码场景,只能由开发介入,复制出一个新的事件,手动修改事件代码后再在编辑器中换绑。
而使用前端逻辑编排,修改方式仅为替换一个逻辑节点,并配置 URL 参数,如下图所示:
逻辑编排扩展了 UI 自助化/零代码系统的能力边界,可能对于开发来说,可视化修改一个逻辑节点不如直接修改一行事件代码来得直接,但对于没有技术背景的用户来说,支持比事件更细粒度的前端逻辑编排能够更灵活地应对逻辑差异,实现了从低代码到零代码到转变。若使用的逻辑节点足够复杂,无论开发还是产品运营都能从中获益。
近年来,业界对 "降本、增效、提质" 的呼声越来越强,业务不得不缩减测试成本。相对后端逻辑,营销活动前端更容易出现 bug,主要是因为营销活动的前端代码面向过程、UI和逻辑杂糅处理的天然特性,导致 逻辑关注点分散,这也使得营销活动的前端代码 CR 非常困难。
据相关机构统计,前端人员每改 3 个 bug 就会诞生 1 个新 bug,不由产生一个灵魂拷问:什么是好代码?个人认为好代码最重要的评判标准是逻辑清晰,意大利面条式的代码会让开发者对代码的修改没有信心。而图形化的配置使得编排逻辑清晰可读,让前端 CR 成为可能。随着成熟的逻辑模块的丰富完善,在降本增效的大环境下,能够同时减少开发和测试的工作量,同时也为逻辑复杂度度量打好基础。
逻辑编排采用基于流的编程范例 FBP (Flow-Based Programming),将逻辑代码封装在逻辑节点中,使用有向线条描述逻辑关系和数据流转,从而实现对代码逻辑、业务流程的可视化编排。
整个方案的架构分为四层:应用层、引擎驱动层、逻辑描述层、可视化工具层。在应用层运行时,会将可视化工具层的编辑器生成的 Logic Schema 输入到逻辑编排引擎中解析编译,执行引擎运行各逻辑节点的功能逻辑并根据 Logic Schema 描述的节点关系进行状态流转。下面对各层一一讲述:
对于不同用户的使用逻辑编排的流程大致如下:
逻辑流程图包含:map 图主体、nodes 节点描述、connections 节点关系描述,这三类实体元素构成了基础的 Logic Schema。
如下图所示的逻辑图,使用 DSL 能够轻松描述并行(环)、串行、异步、分支等逻辑描述,而其中的难点,在于异步逻辑状态的管理。
为了实现逻辑节点的复用及保障每个节点的状态可控,首先需要把包含异步逻辑代码的节点同步化,才能避免因时序问题引发的错误,最优雅的方式莫过于使用 Promise 的 FSM (有限状态机),在 runtime-sdk 的 context 上下文中将 Promise 的默认状态扩展为扩展为 0(默认状态)、1(执行中状态)、2(完成状态)、3(失败状态)、-1(废弃分支状态) 共五种状态,runtime 执行引擎控制节点在五种状态间流转。
逻辑节点赋予状态后,还需要通过执行引擎保障逻辑节点的有序执行。在不同的语言场景下,可以使用不同的算法来解析 Logic Schema。本项目借鉴了洋葱圈模型的思想并进行了一系列改进,以适应当前场景。
洋葱圈来源于 node 的 koa2 后端框架,用于处理框架中间件的链式调用,利用 ES7 的 async/await 语法让异步操作变成同步写法。需要注意的是,洋葱圈模型并非普通递归,递归是直接或间接调用自身,而洋葱圈是使用 compose 插件将下一个中间件作为 next 参数传递,通过执行 await next() 实现了链式调用。每个中间件类似一层洋葱圈,从最外层进入执行到最里层,再从最里层向外执行,所有的请求经过中间件的时候都会执行两次。
在本项目中,将这种处理后端中间件的算法应用于解析前端 Logic Schema 并执行。但是原始的洋葱圈模型是一维的,只能处理逻辑图上的一条线,对并行逻辑、ifelse 无能为力,因此需要结合上述的 FSM,将线性的洋葱圈模型做一些改进用于解析图。
执行引擎对原始的洋葱圈模型的优化处理有如下几点:
对于逻辑开发者,逻辑代码模块格式如上图所示:固定入参为:context
上下文、next
执行函数、params
入参,对后续节点的调用,执行 next(ret);
即可。其中,context 存储了全局上下文及各类全局信息,还有图和节点的运行状态等数据,next 为执行函数,本质上是返回了一个 Promise 对象,用于处理异步时序,而 params 则实现了入参传递。
使用洋葱圈模型将一个节点分两次执行的益处是可以将同类功能聚集在同一个逻辑节点中,避免了逻辑关注点的分散,下面通过动画演示了执行过程。如执行页面 CRUD 操作后,可以在回溯中执行数据查询;也可以将耗时操作延迟执行避免阻塞等。灵活地运用回溯,能够将同类逻辑集中处理,简化开发,减少逻辑图中配置的节点数量。
目前仍有部分浏览器存在 es7 语法的兼容性问题,为了保障客户端执行的兼容性,需要将 db 中存储的 es7 源码编译,降级为 es5 代码在客户端运行。若实时在后端编译,开发环境的效率会很低;若都在客户端做前端编译,在生产环境必然会影响客户端性能,降低用户体验。本项目采用的是开发环境调试工具中前端编译,而生产环境前端活动页面在服务端提前批量编译后存放CDN的方案,实现了性能和效率的平衡。
在前端调试工具中,使用 babel 做前端编译和语法检测,使用 AST 工具做死循环的逻辑检测,使用 Art-Template 做模板引擎前端编译,实现了在 iframe 中纯前端编译调试,效率很高;而最终渲染生成的前端页面中的逻辑代码都是在后端通过 Gulp 脚本批量编译和 Art-Template 模板引擎编译生成的。
逻辑编排调试工具支持节点级断点调试,由于调试工具运行在独立的 iframe 环境中,因此调试工具内外部使用 postmessage + 全局变量的方式实现通信,用于节点捕获、传递逻辑执行信号、结果状态返回等。当在节点属性面板中激活断点后,逻辑执行引擎会在初始化构建上下文 context 时收集断点,当执行到逻辑断点时不再链式执行下去,而是将 FSM 状态控制回调封装为新的 Promise 存储在 handle 句柄中返回,用于逻辑执行方从断点处继续执行。
如下面的动图所示,通过调试运行,逻辑执行路径会变成绿色,日志输出在控制台中,方便开发者调试。
在客户端环境,常规的语法和逻辑错误在调试时可以直接由浏览器捕获csxiaoyao.com定位,而死循环会直接导致客户端卡死后崩溃,非常影响用户体验。由于逻辑代码支持自开发,自由度高,出现死循环的概率也大大提升。本项目在前端调试工具中,解析源码为 AST 抽象语法树,对 js 中的循环体语法注入计时器和计数器后再运行,当循环次数过高且执行时间过长时主动抛出异常,避免了死循环导致的体验问题。
前端逻辑编排和常见的后端流程编排在实现上存在较大差异,由于后端流程编排的逻辑是在服务器上执行的,关注的更多的是性能开销、高并发、可靠性、安全性、事务一致性等,最终暴露输出的产物是一个个逻辑接口;而前端逻辑编排最终将逻辑描述和逻辑代码输出到客户端,在客户端调用逻辑引擎执行编排逻辑,因此更多地关心异步时序、客户端兼容性、扩展性等。
将前端 UI 与逻辑功能解耦重组,通过可视化逻辑编排工具,让csxiaoyao.com没有技术背景的用户能够可视化地通过拖拽连线灵活地实现长链路前端逻辑编辑,满足各类定制化需求,可以弥补目前市面上火热的低代码平台在自助化实现复杂交互,尤其是长链路前端逻辑方面能力的不足。而成熟的逻辑节点的复用不仅迎合了当下降本增效的大环境趋势,也提升了实现复杂功能逻辑的稳定性。