作者:vivo 互联网前端团队- Wang Ning
本文主要从前后端分离的低代码方案、自研高性能渲染引擎、高效的可视化配置方案、千亿级内容投放、低代码如何与传统开发共存等五个维度vivo在低代码平台方面的实践经验,其中也会涉及到动态交互如何运用低代码来编排和我们在提高配置效率方面的全面探索。
一、前言
青春才几年,疫情占三年,后疫情时代,究竟需要什么样的新技术,才能真正解放IT生产力,我认为是低代码,一种可视化的应用开发方法,即“用较少的代码、以较快的速度来交付应用程序”。
低代码如果从表现形式来说确实不是新技术,1980年就有了,但随着前端各种新技术的出现及云原生时代的到来,低代码让我们看到了积极向上的一面;对用户来说:图形化操作,容易上手;内置各种模板、组件,降低开发难度;可视化拖拽,开发效率高。对企业来说:能够缩短产品周期;节省成本,提高效率;而且维护便利,即改即用。低代码的优势这么的显而易见,自然也会在 vivo 发挥它的价值。
随着vivo互联网用户量级不断增加,传统开发已经不能够满足井喷式的运营需求,而后羿,正是我们探索解决方案过程中诞生的用于支撑运营后台业务高效高质量落地的低代码平台,目前已是vivo后台业务首选的在线可视化开发平台,我们在平台建设的过程中也沉淀了大量的经验,后面的内容将会以后羿为背景来详细展开。
接下来我们将从以下五个方面分别展开我们在低代码方面的实践:
- 前后端分离的低代码方案
- 自研高性能渲染引擎
- 高效的可视化配置方案
- 千亿级内容投放
- 低代码如何与传统开发共存
二、前后端分离的低代码方案
低代码平台常常前端部分要占据重头戏,所以在早期,我们采用的是前端大包大揽的技术方案,但随着业务量的剧增,我们遇到了各种各样的诉求,比如后羿侧是否可以输出独立页面,或者支持纯粹的服务端低代码能力、产出独立的接口服务等。为了解决问题及时响应业务诉求,我们大刀阔斧的进行了重构,在后续的版本,我们采用了前后端分离的低代码方案,当然,这种分离包括了“前后端开发分离"和“低代码服务能力分离”,如下图,我们能够直观的看到web开发两种最基本的方式。
前后端分离较不分离的方式,分工更加明确,真正实现解耦;前端可以专注于页面交互、用户体验和兼容性,而后端则主要负责高并发、高可用、高性能、安全、存储和业务逻辑,前后端分离的开发方式也是时下行业的主流选择。我们再来看一下低代码方式开发应用的不同之处。
一种方式是产品视角,或者说是非开发的视角,当我们在低代码平台搭建、开发业务时,无需关心整个制品的具体分层和实现细节,只需要使用平台提供的能力来搭建我们所需的端侧应用即可,这种方式下用户甚至无需具备专业的开发知识便可搭建出简单的应用,这种平台往往也是无代码平台。
另一种则是开发视角,这种思维模式下,用户至少会看到前端和后端两种服务,这两种服务通常来说可能是页面和接口,这种模式更加适合程序员,与日常开发思维保持一致,所以平台学习成本也就很低,能够简单、快速的开发出更加复杂的应用;后羿主要面向开发者,自然而然的采用了这种分层开发的模式。
低代码平台本身也需要开发者投入大量的开发精力,一个好的开发模式往往能够事半功倍,目前流行的低代码产品,大多是下图所示两大类实现方式。
前后端不分离实现会导致平台的灵活性差、拓展性差、可集成度较低;反观前后端分离实现的方式,我们可以设计简单易懂的DSL,下发到开发侧编译转换,发挥各自的优势;前后端版本迭代和优化升级也可以做到互不干扰。
正如上图,得益于前后端分离的分层架构,我们在前端服务层又分离出开发者平台和运营平台;开发者平台专注于可视化搭建,运营平台面向最终的业务运营;一个负责开发体验,一个负责用户体验;后端则通过微服务架构拆分出不同功能模块,实现了平台逻辑与业务逻辑的解耦。
前后端分离的方案,分层明确,解除耦合,而且前后端各自的服务也实现了逻辑分层,得益于这种架构,我们很轻松就实现了前后端低代码能力的分离,来满足更加复杂的业务诉求。
前文我们提到,前后端分离中还包括了前后端低代码服务能力的分离。
如上图所示,开发者平台产出的DSL,传递到端侧,经过各自的运行时解析,便可以针对不同用户提供不同的低代码能力;这样,用户就可以使用平台搭建页面来连接自己的服务,或者编排接口来为自己的页面提供存储服务;既可以单独配置页面,也可以独立使用接口服务,这就是前后端低代码能力的分离,前后端分别配置,也与传统开发逻辑、思维方式一致,对开发者十分友好。
除此之外,前后端分离的方案,也带来了其他的一些利好:前端侧通过引入BFF层可轻松实现动态接口代理、鉴权、日志;服务端也可以做接口的微服务化;通过功能拆分、组件懒加载等方式可以提升性能;也能够更好的与传统开发兼容,各施所长;前后端独立部署更加灵活、高效;也更易被第三方应用集成。
三、自研高性能渲染引擎
渲染引擎是由动态表单渲染器、列表渲染器和动态交互解释器三部分组成的,他们能够各司其职也可以相互配合,渲染引擎的主要作用就是将可视化操作生成的DSL翻译成具有功能逻辑和交互的页面、模板或组件。
先来看看表单渲染器,
众所周知,表单场景一直都是前端中后台领域最复杂的场景,通过自研的表单渲染引擎我们提供了表单数据管理、表单状态管理、动态渲染、组件联动等功能;基于JSONSchema驱动的分层架构,实现了逻辑与UI框架解耦;通常,用户只需要稍微了解几个胶水层的API便可以快速上手;复杂的场景下,用户还可以通过拓展组件属性或开发自定义组件的方式来满足需求;另外,我们还将表单实例挂载到了动态交互的上下文,这样我们就可以很轻松的实现各块级组件联动和数据交互。
特别说明的是,自研完全是为了更加贴合业务需要,开源社区有很多优秀的动态解决方案,如formily2、x-render、formast,他们都有各自的优缺点,我们也是权衡了利弊之后选择的自研,当然我们也借鉴了x-render的api设计与formast的动态语法表达式,我们追求的是简单、好用、高性能及完全可控。
再来看看列表渲染器,
列表是前端中后台领域又一个非常重要的场景,为了满足各种各样的列表需求,我们二次开发了vxe-table这个功能丰富的开源列表库,各种工具,复杂表格、树形表格、编辑表格、虚拟滚动(ps:自定义渲染器的场景大数据的性能表现不佳)都是天然支持,我们额外内置了图片、视频等15种常用的渲染场景;与表单渲染器相同,列表渲染器依然是基于json-schema驱动的分层架构,学习成本极低,拓展简单,也支持用户自定义渲染器;同样,我们也将列表实例挂载到了动态交互的上下文,实现与其他块级组件的联动和数据交互。
说到列表,我们提一下图表,图表你也可以理解为列表的另一种展现形式,有了列表的开发经验,图表实现起来也十分轻松,只需要设计合理的DSL编译后下发给第三方库即可(如Echart),主要的思路还是和表单进行联动,由表单来驱动查询条件,执行异步查询,得到的数据经过格式化后绑定到图表即可。
有了表单和列表,已经能够搭出简单页面了,但是弹窗、按钮交互、接口请求如何实现呢?动态交互是前端低代码最复杂也是最有趣的部分,下面就来揭开它的神秘面纱。
如上图所示,由用户点击按钮发起,弹出表单弹窗,填写表单,发起接口请求,根据响应结果提示和列表刷新,其中有的是用户交互,有的则是程序在驱动;我们通过对这样的动态交互流程建模,可以抽象出流程源和一个个流程节点;当用户触发交互,一个个交互节点组成了动态交互队列,有序执行,虽然实际情况可能会更复杂,有异步、有分支,但我们也仅仅通过不到30行的代码便实现了整个动态交互的驱动,我们把这个核心解决方案称之为动态交互解释器,如下图所示伪代码。
同样,动态交互解释器也是基于JSONSchema驱动的分层架构,解释器仅仅是一层胶水和内置的交互流程节点;执行器主要的功能就是贮藏动态节点、传递动态上下文、解释执行动态交互、流转或终止流程。
前文我们多次提到了“将实例挂载到动态交互上下文”,正如伪代码中的ctx,这是一个响应式的上下文,我们会根据不同的业务场景有选择性的挂载表单、列表、图表的实例及相关方法和诸如路由信息、全局状态、应用信息等其他用户可能会需要的重要数据,以便各流程节点可以实时的访问实例和动态修改对应的实例,这样就实现了各区块间的联动交互。
动态交互解释器也支持自定义,在极其复杂的场景下我们可以通过添加自定义流程节点的方式来拓展功能,满足需求。
四、高效的可视化配置方案
不同于其他低代码平台,在后羿中,我们将页面视为资源,按照资源级别来管理、发布我们的配置,这样做的好处有两个:
- 第一、 我们可以根据资源的层级关系设计不同的导航风格,可以是tab-history模式,也可以是面包屑模式,以及你能想到的任何菜单管理模式。
- 第二、 资源的管理与页面的可视化配置解耦,管理更加高效;如上图所示,除了可以随时拖拽调整菜单结构,还可以一目了然的看到资源的详细信息;得益于这种设计,我们提供了针对资源级别的版本发布功能,可以实现一键迭代及线上热更新;基于V消息的工单版本管理,安全高效可追溯,还能够实现秒级回退。
如上图,我们还提供了模板、代码片段功能,模板专注于同类型页面的复用,代码片段则专注于组件、功能逻辑的复用;通过复用,可以极大的降低开发时间,5分钟搭建页面不再是纸上谈兵。
系统功能上我们提供了一键开启常用的水印、菜单搜索、消息通知等功能,还可以配置多种类型客服信息,方便系统级的版本发布通知及日常的值班人员维护。
页面内容的配置我们采用了大家最习惯的从左到右的拖拽配置方式,可视化的配置方式便捷、高效,而且实时拖拽,即刻预览。
动态交互同样支持可视化配置,流程的运转逻辑清晰的展示在画布上,直观又容易维护。
此外,我们还提供了一些贴心的功能:内置动态接口代理,一键开启后,即可连接本地或mock服务,开发调试非常方便;统一的服务配置入口,除了符合开发直觉, 也方便了系统层面的接口管理及复用,系统还会根据不同环境自动执行接口匹配。
我们提供了页面结构大纲视图,只需点击icon,便可以快速定位到组件,解决了复杂页面查找组件的痛苦;
开发或迭代时,对页面的改动无法追溯也是一个痛点,于是我们内置了版本比对,只需拖拽任意两个版本到比对框,就可以实现两者的精确比对,方便排查问题;每个版本也提供了版本快速回退,点击即可一键回退。
右键功能也是提高配置效率的法宝,基于右键,我们提供了组件的复制粘贴,并且可以跨区块、跨页面、跨应用的复制粘贴;右键也可以快速定位到组件的schema,修改schema也会实时同步到视图;代码片段的保存复用也是基于右键来提供。
得益于开发者平台的分层设计,只需按照编辑器协议配置,自定义组件同样可以享受可视化的配置能力。
多层嵌套配置,是可视化中相当痛苦的场景,于是我们提供了扁平化配置方案,比起层层堆叠的弹窗配置,配置更加方便,切换成本更低。
另外我们对新手用户也十分友好,除了引导式配置,我们还提供了字段级功能说明及文档指引,以降低配置门槛。
说到文档(如上图),这可能是很多低代码平台都会遇到的问题,我们认为一个好的文档必须要能够指引用户由浅入深的学习平台的使用姿势,否则会直接劝退一大批用户,我们的用户主要面向开发者,这里面还分离出前端、后端、应用、AI、大数据等等,如何让各岗位的同学都能够找到想要的解决方案真的是很棘手,于是我们由浅入深,层层展开,简单到手把手教学,深入到整个核心库的原理及实现,并且还提供了海量的示例,包括数据联动、动态交互、布局等等。
五、千亿级内容投放
内容投放是否高效会直接影响用户的选择,后羿通过通用的 CURD 接口及可动态插拔的业务模块来实现数据的存储和处理。在用户完成操作后统一执行数据处理和入库,并使用独立的投放服务来快速分发到各业务系统。
对于五花八门的运营数据我们会无差别的存放在MongoDB中;通过自定义的分仓策略来保证业务隔离和可扩展;当然也会涉及到数据的多级关联,自定义检索等,多种手段的加持下才达到最后的精确分发。
后羿平台承载了海量的业务数据,面对巨大的用户流量,我们必须保证投放的高可用。
如图所示,我们在架构上采用独立镜像服务来承载各个大流量业务,各独立服务又有本地缓存、磁盘缓存和独立Redis集群来保证单体服务的高可用。
除了高可用,还要能够支持高并发,目前我们的QPS在百万级别,每次请求可能会关联查询上百个表单,最终就会放大到千、万亿级别的表单查询量。
我们通过异步加并发的方式提升服务的吞吐量,结合异步监听、动态更新、定时重新加载等方式来提升系统的性能;多种手段的加持最终保证了服务的高并发。
对于个性化的业务诉求,我们还支持在后羿提供的SDK 上二次拓展,这部分与传统的开发几乎没有区别。
六、低代码如何与传统开发共存
说到传统开发,那我们就来聊聊这个老生常谈的话题:
- 低代码如何与传统开发共存?
- 低代码会取代程序员吗?
- 低代码会不会干掉传统开发?
- 首先我们要明确的是,两者并不冲突!
低代码也不是银弹,而传统开发有着天然的定制化优势,灵活且没有限制,配套的技术也相当成熟;所以我们认为两者共存,优势互补才能发挥更大的价值。
那后羿是如何实践的呢?
一方面我们不断的丰富场景模型,提高拓展能力和配置效率;另一方面则从底层架构设计上兼容了传统的定制化开发;我们双向支持iframe及微应用,双向意味着后羿产出的页面可以嵌入到第三方应用中,也接受第三方应用嵌入到后羿中;并且支持页面级、区块级和组件级的嵌入。
这种设计除了可以发挥传统开发优势,还能让现存的老、旧应用发挥余热,简单改造,就可以将他们集成到后羿,然后在此基础上使用低代码能力继续维护;得益于后羿将菜单与页面内容隔离设计的方案,我们可以轻松的实现与第三方应用的兼容,不破换其自有的菜单管理体系。
传统开发场景,为了让大家专注于业务逻辑,我们打通了树懒的资源快速部署能力,并且提供了多种类型的工程脚手架,支持脚本命令一键发布迭代;另外还支持素材托管,拥有独立的业务空间,安全又便捷。
以上就是后羿的低代码实践经验,这么短的篇幅不足以揭开后羿的全貌,对于低代码来说也只是杯水车薪,我们利用现有的资源、服务、基建(基建真的很重要)以最小的成本孵化出来了后羿低代码平台,其实能做的还很多,我们也会持续探索,让每个人都能享受到低代码的乐趣。