逻辑编排在前端可视化搭建中的实践

·  阅读 6061

背景&价值

目前公司内外部有多面向后端的逻辑编排技术专项,且没有统一的标准、沉淀通用的方案。我们前端同学经常会尝试页面级、模块级的复用,减少代码重复开发,减轻人力资源投入,提升开发效率。那我们是否也可以拆分到更小粒度,函数层的逻辑复用呢?

我们团队近期打造了具有前端特色的逻辑编排方案,建立令用户自由且规范化编排逻辑的编排器,以no code的形式,借助图形化组件,令产品、运营同学可搭建生产逻辑,并可复用于多业务场景。

本文通过逻辑编排可视化搭建的设计与业务实践,来和大家做一些交流。

编排器

基础概念

编排器是逻辑编排中通过可视化搭建,生成一条具有业务逻辑的容器;将逻辑流程图导出,通过DSL转化,生成业务中实际执行的代码逻辑。可以理解为逻辑编排中生产逻辑的环节。

平台侧编排器设计为如图四个部分。工具栏、元件列表、搭建画布、基础配置。

image.png

工具栏包含保存逻辑、检查搭建规范性和导出逻辑这些基础操作。元件是逻辑中的一个节点,一个函数,是将业务中常用的逻辑抽离出来,按照编码规范沉淀下来。如下图列表中有不同形状色彩的元件,是我们根据函数类型对元件的进行的分类。

中间部分是进行逻辑搭建的画布,主要操作动作是将逻辑元件拖拽进来,通过具有指向性的线条将元件连接起来,组合成一条完整有始有终的逻辑,实现简单的或者复杂的具有特定业务含义的流程图,也是核心部分。

右侧是点击某个元件所显示的对应基础配置信息面板,即函数的入参,我们的设计一定是令用户可灵活配置元件基础信息。

image.png

在设计该编排器的时候,我的目的是通过技术手段,建立令用户最大化自由编排且规范的编排器,使之无侵入、可复用各业务场景。下面介绍我们是如何具体设计的。

整体设计

元件设计

基于G6绘图引擎能力,根据元件定义类型,注册出不同形状、色彩、结构的元件。元件由主体形状、分支线、描述文字、节点四部分组成。其中节点如图也分上下两部分,上方节点是其他元件连接至当前元件的节点,下方节点是当前元件可连接至其他元件的节点。

image.png 为了令用户更丝滑且更标准化地搭建逻辑,我们在设计元件时将一切限制条件,得已显示,在用户所有可执行的动作中,得已体现。例如我们在初始化元件环节,开发者可设定好该元件会有几种输出情况、几个分支,每个元件可连接几个其他元件,每条分支代表哪种结果导向,定向去做关联。所见即所得,不让用户去猜或任意自定义连接。这样设计的目的是,减少用户操作成本,减少解释成本,增强搭建规范性。

元件类型

image.png

根据元件是否含有逻辑,将元件分为两大类:基础元件、业务元件。如上树形图。

基础元件不带有任何业务逻辑,包含开始、结束元件,固定为一条逻辑的起点或终点标识,每条逻辑必有开始元件和结束元件。把基础元件设计为圆形、绿色;业务元件均带有业务逻辑,仅有一个输入节点,依据元件可输出的结果数量,将业务元件又更细粒度地分为三种类型。(详情如下表)

结束业务元件,虽然带有业务逻辑,但也可能是一条逻辑的终点。如跳转新页面逻辑,这个动作执行后会跳转到一个新地址,无法在当前页面当前逻辑继续传递至下一动作,所以称之为结束业务元件,所以设计它为仅有一个输入节点,无输出节点。因为同样标识一条逻辑的结束,我也将它设计为圆形,用黄色作为区分。

普通业务元件就是一个正常有输入且有固定一种结果导向的元件,比如通过接口请求获取数据,会有固定schema格式的返回数据输出;所以普通业务元件有一个输入节点,一个输出节点,形状为长方形。

判断业务元件会复杂点,根据元件设计规范,在创建元件时即可确定有多种结果输出,该结果为>1种,且也会有固定schema格式的返回数据输出。如判断用户是否登录,分为已登录、未登录两种情况;判断业务元件也不是非1即0的情况输出,也可能会有a 、b、c、d...多结果导向。因此普通业务元件有一个输入节点,N个输出节点,形状设计为菱形。

image.png

我们根据函数类型定义、注册元件,尽可能涵盖所有的类型,目的就是让这套逻辑编排方案通用化、可复用,适用于多业务、多场景。

注册元件

元件有多种形状、颜色、分支、节点不定,平台侧注册元件的思路:拿判断业务元件为例,使用SVG绘制菱形 - 元件主体形状,根据元件类型自定义元件样式,根据元件的分支数量,动态绘制分支线的长度、节点位置、锚点信息等等。

G6.registerNode(
  'diamond',
  {
    draw(cfg: Cfg, group: GGroup) {
      const size = [94, 64]; // 转换成 [width, height] 的模式
      const width = size[0];
      const height = size[1];
      // svg 自定义主体形状
      const path = [
        ['M', 0, 0 - height / 2], // 上部顶点
        ['L', width / 2, 0], // 右侧顶点
        ['L', 0, height / 2], // 下部顶点
        ['L', -width / 2, 0], // 左侧顶点
        ['Z'], // 封闭
      ];
      ...
    },
    afterDraw(cfg: Cfg, group: GGroup) {
      const size = [100, 64];
      const width = size[0];
      const height = size[1];
      // 分支描述信息
      addDesc(cfg, group, width, height, cfg.outLine);
      // 分支线
      addLine(group, width, height, cfg.outLine);
      // 连接锚点(小圆点)
      addNode(group, width, height, cfg.outLine, cfg.showNode);
    },
    ...
  },
  'base-node',
);
复制代码

元件分支线也是长短、方向各异,线的长度由分支数量决定,即代码中outline字段:

const addLine = (group, width, height, outLine) => {
  group.addShape('path', {
    attrs: {
      path: [
        ['M', -width / 2 * (outLine - 1), height + 3],
        ['L', width / 2 * (outLine - 1), height + 3],
        ['Z']
      ],
      ...edgeStyle
    },
    draggable: true,
  });
}
复制代码

元件的节点分为一个输入节点,一组输出节点,输出节点的位置依然由outline决定

const addNode = (group, width, height, outLine, isShow) => {
  // x坐标
  let xPosition = i => {
    return -width / 2 * (outLine - 1) + width * i;
  }
  outLine && new Array(outLine).fill(0).forEach((d, i) => {
    group.addShape('circle', {
      attrs: {
        x: xPosition(i),
        y: height + 4,
        r: 4.5,
        ...dotStyle
      },
      name: `circle-shape-${i + 1}`
    });
  })
}
复制代码

创建元件

用户需要根据规范,去创建一个新的元件,初始化项目包含四个核心文件,拿基础业务元件举例,如图所示。

image.png

index.js 是元件的主体函数逻辑文件,函数一般是有入参的,且可以让用户灵活配置。input.json 文件即定义为函数基础配置信息的标准Schema协议,该内容在平台侧可渲染为表单,供用户配置基础信息,在逻辑执行过程作为payload参数传入对应函数。

基础业务元件,函数执行有固定一种结果输出的情况,那branch.json这个决定分支数量与分支信息的文件就有数组长度为1,只输入一项内容,录入即可。

output.json 是主体函数执行后最终return返回的data数据对应的标准json schema协议信息。输出数据是逻辑编排的产物,如图,可供业务侧绑定对应字段,可视化显示出对应内容。

image.png

不同元件类型,项目结构是一样的,不同的是,输出结果的数量不同,主体函数异步返回的内容结构有区别。

如检查登录元件,会返回已登录、未登录两种情况,那主体函数会调用相应的api后,经过判断在两种返回情况里做选择。返回信息branch为0,即为branch文件对应的数组第一项的未登录的返回情况; branch为1,即为branch文件对应的数组第二项的已登录情况,返回data也对应已登录的消息提示信息。Branch文件的数组长度即为元件输出结果的数量,数组内容也是语义化对应的。这就是元件整体结构的设计与对应开发录入规范,开发者根据这样的规范即可创建对应类型的新元件。

input、output文件的schema标准协议数据我们也提供了【可视化搭建 表单设计器】供开发者快速搭建出对应内容。

基于form-render的可视化搭建

我们平台嵌入了基于 form-render的表单设计器,可联动、自定义组件快速接入,满足多元定制化需求。考虑到form-render在 schema 的设计上,寻求合适的现有标准作为载体在其上适度扩展,经过长时间积累已经可以覆盖大部分业务场景,所以基本可以实现我们的业务诉求,即可能会有的扩展,所以选择该schema to form方案。

技术同学可根据功能需求,通过可视化拖拽的方式搭建出配置表单;点击导出Schema,即可导出标准json schema协议信息,这就是元件注册环节的基础配置信息InputSchema协议来源,也是输出数据outputSchema标准协议信息来源。

image.png

提供搭建的元件市场

逻辑编排的优势是将逻辑与UI解耦,并且职责划分明确;开发同学的职责是开发元件逻辑,对元件进行发布、录入元件市场、状态管理、迭代版本管理。产品同学根据需求,在元件市场选择已有元件,若无对应元件向开发同学提需求,然后进行搭建逻辑,生产逻辑供业务侧使用。

image.png

逻辑编排

搭建逻辑,是将元件列表中的元件拖拽至画布中,通过具有指向性的线条将节点与节点对应连接起来,即可将逻辑片段组合成一条完整有始有终的逻辑。(效果展示)

image.png

通过技术手段,增添了搭建智能对齐、检查规范、自动纠错等能力。点击工具栏中检查按钮、保存按钮,都会根据规则对不规范搭建行为提示说明,例如元件基础配置表单某字段不可为空;在用户搭建过程,我们也设计了元件连接规则,对不规范搭建行为自动纠错,例如元件不可闭环连接、不可重复连接等。我们建立令用户最大化自由编排且规范化的编排器,使用户在搭建、发布逻辑时,已经是规范可使用的逻辑。

image.png

当然我们在设计编排器上,也将物料列表、画布、属性面板,做解耦处理;每个功能面板不会强依赖于另一个,有通用适配器,设定好的基础规范与可供选择使用的工具,也有可根据不用业务场景做定制化的板块。我们的初衷是,以逻辑搭建为中心,可根据实际业务场景打造不同的产品形态。

业务落地

UI&逻辑编排结合设计

我们首个尝试落地的业务平台也是我们团队的好莱坞平台,与UI编排的结合尝试。UI编排,也称为通用热区模块,是平台推出的模块生产工具,提供易用、强大的操作界面,让运营和设计同学像制作PPT一样制作模块。如图,UI编排完全可实现一个静态页面的搭建;用户可任意创建热区模块,热区作为一个载体可配置文字、样式、图片等基础信息,在对应热区绑定“动作”,动作的概念就是用户主动点击后所触发的一个行为,如跳转页面、关闭页面、打开小程序等等。这是纯UI编排搭建。

image.png

本次寒假战役的一个模块使用UI编排进行了搭建尝试。我们来看下UI编排与逻辑编排结合起来的整体设计。

在模块中触发逻辑分为两种情况,一种是主动触发逻辑,即伴随组件加载自动开始执行的逻辑。一种是被动触发逻辑,需要用户点击才可触发执行的逻辑。在对应功能配置区选择提前搭建好的逻辑即可将逻辑挂载到当前模块。

如图,每一条逻辑都拥有独立上下文,逻辑执行后对应的数据产物,会存于上下文并与当前模块共享;模块将所有数据以key.value的格式打平,通过语义,将视图中的热区绑定对应字段;一旦逻辑执行过程中数据状态发生改变,就会驱动模块的局部UI动态渲染。若模块的状态绑定了对应字段,那字段数据状态发生改变,也会驱动模块整体UI改变。

image.png

平台侧实际操作,如下图右侧,热区选择动作中的“逻辑编排”,二级下拉框选择一个 从逻辑编排平台搭建好导出的逻辑。

image.png

逻辑的数据产物根据outputSchema协议可视化展示在业务平台侧,对应热区模块可选择/绑定对应字段内容,逻辑真正执行时,数据驱动视图改变。

image.png

前后对比 - 有什么优势

从前:产品提了需求后,前端开发同学需要依赖设计稿,开始页面开发&逻辑开发,开发完成后发布、录入模块至平台侧,运营同学方可配置模块基础信息。

现在:在了解需求后,前端&设计可并行启动工作了,前端同学开始开发该模块需要依赖的逻辑元件,开发完成后产品根据需求搭建出逻辑;设计同学也可开始绘设计稿并在平台侧可导入整体样式,进而运营同学去配置基础信息、对应动作选择匹配的逻辑。

这样的优点是:项目进行中,UI、逻辑的频繁改动不会太过于依赖技术开发、技术发版,将重开发拆分为多角色轻工作快迭代; 我们从模块复用,下沉到更小粒度的元件复用,释放研发人员的 "搬砖" 工作。经验证,传统源码开发从接到需求到上线大概需要评估2天时间,通过UI逻辑编排结合的方案仅需要2h。真正实现降本提效。并且最大的优点是做到逻辑与UI的解耦。

image.png

当UI设计稿有变动时,前后流程的对比:

纯UI改动已经完全释放开发资源,节省源码开发 -> 代码发布 -> 模块录入/上架 -> 运营升级模块的流程,既节省开发人力成本,运营同学通过增/删/改配置信息也不再依赖开发。

image.png

虽然逻辑变动看似多了一个判断步骤,但若已有可复用的逻辑元件,产品直接拖拽更新逻辑即可;无可复用元件需开发同学新开发。但长远角度思考,我们从模块复用,下沉到更小粒度的元件复用,元件库不断沉淀着逻辑,未来元件复用率一定是增长的。我们从开发角度,也在规划从本地元件开发升级为在线开发,一键发布并上架元件。让流程编排效率更大一步提升。

image.png

结构图

这张结构图展示了逻辑编排与业务平台侧整体上下游关系

逻辑编排分为编排器、DSL、Runtime三部分:

  • 编排器基于Schema解析和G6绘图引擎,结合对元件类型的定义注册绘制出不同形态与功能的四种元件,供画布搭建使用;元件是由开发者开发,且可在元件管理页面对元件进行管理。画布包含缩放、撤销等工具栏功能,用户搭建过程,我们通过技术手段提供智能对齐、检查规范、自动纠错、在线测试等能力,让用户更顺滑且规范地搭建出逻辑。

  • DSL 转换器 将画布所有信息转换为json,提供给业务方

  • 业务平台侧逻辑的起点在主动触发逻辑&被动触发逻辑,一旦逻辑被触发,由Runtime提供的api进行注册元件,并开始执行,每一个元件的执行对应的数据产物会通过context与业务共享,一旦绑定字段对应的数据发生状态变动,会引起局部UI变动,模块整体状态切换。动作执行完流转到下一动作,如此直到逻辑结束。

这就是逻辑与UI结合的整体 / 核心思路,逻辑编排产生数据,UI编排监听数据变化,驱动试图改变。

image.png

总结

逻辑编排在以下三个方面做出了突破:

逻辑可复用:避免研发同学重复代码开发,节省人力成本,实现降本提效,也使需求变动成本降低;

分工更明确:实现了UI与逻辑解耦,技术在开发环节仅开发逻辑,将重开发工作,拆分为多角色轻工作的配合。且产品搭建逻辑的同时,也将需求可视化,便于他人理解;

业务可复用:通过对元件、画布、DSL、Rumtime的通用化、灵活性且可扩展地设计,使可适用于多业务场景。我们的规划是,以逻辑搭建为中心,可根据实际业务场景打造不同的产品形态,希望可以服务更多平台。

image.png

我们的逻辑编排技术专项目前在落地业务的起步阶段,以及功能迭代拓展,欢迎大家交流~

分类:
前端
标签:
收藏成功!
已添加到「」, 点击更改