基于 LowCodeEngine 的低代码组件体系的建设和实践
今天在这里和大家聊一聊前端组件,或者现在更流行的说法「物料」的话题。
物料本身已经不是一个新鲜的话题了,从 06 年 jQuery 发布,前端物料就开始以各种 jQuery 插件的形式不断涌现,直到今天我们仍然可以在 github 上看到很多 jQuery 物料插件,他们当中的设计思路在今天流行的这些前端组件库里仍然可以看到很多影子。
然而,物料也是一个常聊常新的话题,回看前端的发展历史,会发现伴随着前端技术的发展,新的模块化方式、工程化工具、研发框架的出现,物料的组织形式和品牌都在不断革新变化之中。究其原因,我觉得在于物料作为前端的一种模块化抽象,是最明显和快速的提效手段。前端团队和个人在业务开发到一定程度时,使用抽象物料的手段来降低业务开发的重复性和复杂度几乎可以说是一条必经之路。此外,物料的抽象往往又和模块化、工具、框架这些技术手段紧密相连,这些技术的革新也会催生新的物料生产和消费形式的诞生。
近些年来,低代码领域的话题热度在不断升温,国内外的各大云厂商和传统 SaaS 厂商都在低代码领域不断加码投入。在这样的时代大背景下,物料这个老生常谈的话题,又被赋予了哪些新的问题,伴随着哪些挑战和机遇。这里面的思考和实践正是今天想和大家聊的东西。
物料的现状和挑战
后 jQuery 时代,以 React、Vue、Angular 为代表的的前端框架已经站稳脚跟,经过数年的迭代更新和互相学习,API、使用方式和生态都已成型并基本完善。同时,基于三大框架的基础组件库,无论从组件分类、使用方式、主题定制以及相关的生态上都已经比较完善,并且都已经有了自己的忠实拥趸和重度使用者。
从基础组件到业务组件
大部分的前端团队已经很少再从零开始写基础组件库,挑选一个合适的符合自己业务特点和技术选型的基础组件库,并基于此生产适合自己业务的业务组件库,相信是更多人的选择。通常我们会从四个角度出发去封装我们的业务组件库:
业务数据:将基础组件与业务数据接口结合,如选人、选部门、选城市等 业务场景:业务中出现的一些通用场景,以大颗粒度的形式封装起来,常见于表格、表单、图表场景 业务特色:业务里独有的交互形式,可以由一些基础组件拼合而成 品牌主题:将基础组件修改为和业务产品品牌设计相符合的样式
从技术的角度看,业务组件与基础组件主要面临的问题不同:
业务组件对易用性和开箱即用的诉求高于扩展性和通用性 业务组件大多不需要处理复杂的交互 业务组件对业务频繁变化需要做出快速响应
低代码时代的全新挑战
无论我们如何看待低代码的问题,低代码的浪潮正在不可逆转地到来。Gartner 预测,到 2024 年,65%的应用开发活动将由低代码开发完成。越来越多的企业开始认可并接受低代码的工作方式,大型云服务厂商都已经上线了自己的低代码平台。
在低代码时代,对我们现有的物料体系提出了挑战:
如何能够为低代码项目提供丰富的物料? 如何低成本将已有的物料接入到低代码项目中?
同时,我们也有机会去思考,是否可以使用低代码技术去更高效、更高质量地生产业务物料。
这也引出了这篇文章核心要讨论的两个问题:
Q1:如何让物料为低代码引用服务?
Q2:如何让低代码技术为物料生产服务。
已有物料和低代码
首先我们先讨论如何将已有物料快速导入到低代码项目中使用的问题。在低代码时代之前,其实我们已经积累了大量的物料,在低代码应用中,我们希望能够在组件面板中找到这些组件,并且可以配置这些组件的属性来使用。下图展示的是普通物料的使用文档和低代码应用中物料的配置界面。可以看出,普通物料和低代码项目的物料的差异主要在于,除了正常的组件渲染逻辑外,低代码应用需要物料的名称、描述、截图、logo 等信息,以及哪些属性可以供用户配置和更改。
因此我们只要给一个已有物料关联上一份上面描述的配置信息(后文称「物料描述配置」),就变成了一份可在低代码应用中使用的物料(我们称之为「可搭建物料」)。同时这份配置应该和已有物料分离,这样可以实现对原有物料最大程度的复用,而不需要修改原有物料。
既然是配置,就要有协议规范,只有在协议规范的指导下,才可以实现多个环节的最大协同,而不至于实现上出现分歧。在我们开源的 LowCodeEngine 项目中,我们也同步开源了低代码应用最重要的三个协议规范:「低代码引擎物料协议规范」「低代码引擎搭建协议规范」和 「低代码引擎资产包协议规范」。其中「低代码引擎物料协议规范」就重点对物料描述配置进行了约定。
我们通过一个协议数据和低代码中组件实例的对照,来更实际地感受一下物料描述的内容。
目前,我们已经有 1800+ 的组件,通过手写物料描述的形式接入了低代码项目中。继而我们在思考一个问题,既然物料描述本身是一份协议约定的结构化数据,其本身是否也可以通过低代码技术来生产呢?这样可以进一步地降低组件接入低代码引擎的成本。
物料描述的可视化编排
我们其实仔细看下组件的属性配置部分,就会发现他其实也是一个经典的表单,继而我们想到我们可以通过搭建一个表单页面地方式来生成一份物料描述,只是这个表单里的每个组件表达的是一个属性的名称、类型、描述、设置等。我们就可以把这样一份页面搭建的描述转换为物料配置的描述。
当然我们还可以更近一步,首先分析源码组件的属性定义,如 React 组件的 propTypes 和 defaultProps。用这些定义生成搭建的模板,进一步提高生产的效率。
这是我们使用中的平台界面,最终我们使用这种低代码地方式生产了 1200+ 的物料描述,让这些源码组件可以快速地进入到低代码应用中。
低代码生产组件
之前我们主要讨论的是已有物料接入低代码项目中的思考和实践。接下来我想聊一下,如何基于低代码技术来生产组件,这也回应我们前面对于物料现状的讨论,如何高效率地开发业务组件。
我们前面有提到,业务组件通常是基于基础组件之上完成的,封装接口、业务逻辑提高复用性是他的主要场景,所以大部分没有复杂的交互,但相比起基础组件来说,变更会更频繁。用低代码生产很高地适应了这些特点,它具备极低的上手门槛,更高的研发效率以及对低代码项目天然的友好性。
让我们看看为什么这么说,关于上手门槛,首先低代码平台是完全 web 端操作的,就跟访问其他网站一样,不需要开发者准备他的开发环境,如 node、python、git 等,随到随用。其次,他可以屏蔽很多开发的技术栈细节,如 React、Redux 等等,通过良好的封装性和精心升级的各种工具来实现组件开发中的各种细节(如属性配置、请求数据点等)。在研发效率方面,低代码生产组件不需建立仓库,也无需构建等环节,随到随开发,本身就十分轻量级,因此也可以更好地应对频繁变更的需求。由于本身是依靠低代码开发的,低代码产生数据的结构性,也可以让他在定义属性的同时就做好了物料描述,因此可以直接在低代码应用中使用,而无需再经历我们上面讨论的环节。
除了这些优点之外,他的轻量性决定了也更容易复制和扩展,他的全程平台化可以更好地保证代码的安全性,并且研发链路的多个环节可以管控,对于应用的质量和稳定性也会带来便利。同时他也不是只能在低代码应用中使用,我们也可以导出成一个源码 React 组件,在源码项目中使用。
接下来我们将详细讨论如何使用低代码技术生产出一个可以使用的组件。
组件是什么?
我们要想实现组件生产到使用的完整生命周期,首先要搞清楚组件是什么。前端组件并没有写进规范中的定义,但在前端大量的生产实践中,一个前端组件大多具备以下特性:
封装性:逻辑和 UI 被封装在一个黑盒中,对外部使用者不可见,外部使用者也无法直接修改被封装的逻辑和 UI。 接口性:由于封装性的存在,这个黑盒需要暴露一些对外的接口,从而实现与外部的通信以及外部对组件的控制。 切面性:组件从初次渲染到最终卸载过程中会经历多个阶段,通常情况下需要提供这些阶段的控制能力,如生命周期的概念。 独立性:组件不必和应用的维护和发版保持一致,可以独立维护和发版。 复用性:组件不会只被应用消费,本身应该可以引用其他组件或者被其他组件引用。
具备了这些特性的抽象,我们一般可以称之为一个组件。接下来我们细化看一下为此我们要实现的功能。
首先抱着协议先行的原则,我们定义了一份低代码组件的搭建协议,用以驱动 UI 渲染。低代码组件的搭建协议和页面类似,但在他的基础之上主要增加了对于属性方面的定义。
在能力分层上,我们将需要构建的能力分为四个层次。基础能力的部分是一个组件必备的部分,用于实现组件最基本的功能。增强能力则是有了之后会比没有的情况下更易用的部分。预览调试的功能用来帮助用户开发地更顺畅。而发布消费则是在组件生产结束后如何给到项目中去使用。接下来我们会对这里面的一些实现进行重点讨论。
属性的实现
属性是组件对外的接口,在低代码组件中,我们可以通过可视化的方式来注册属性,包括属性的名称,描述,类型,使用哪种类型的设置器,以及是否需要联动等。这些设置可以自动转化为组件的物料描述。实际渲染的过程中,我们创建了一种特殊的组件状态 props,组件内可以直接绑定 props,或者在逻辑块中通过 props.xxx 来获取。
生命周期
我们规定了组件里五个最常见的生命周期用以实现组件的切面。这些切面,不同的框架可能有不同的实现,但是都必不可少。我们在低代码组件的最外层容器组件上对这个五个声明周期进行实现。
预览和调试
我们提供了两套开发调试环境,一套称为”预览“,在这个环境里可以简单地对属性进行修改,比较轻量级。但无法体验组件的设计态(组件被拖入项目设计态时的状态)。所以我们还有另一套称为”调试“的环境,在这个环境中,我们会帮助用户创建一个模拟项目环境,并安装上这个组件。用户可以从设计态到预览态,完整调试低代码组件的整个过程,并且可以和其他的组件进行联动。同时,调试的内容也可以用来生成 demo,供组件使用者查看
组件的消费
低代码组件在低代码应用中的消费,主要需要考虑如何实现组件的封装性。
我们实现了一个组件渲染器,类似页面渲染器,同样可以根据 schema 递归渲染节点。在注册组件的时候,通过判断组件是否包含 schema 信息来决定是否注册为低代码组件,低代码组件在页面渲染器渲染 schema 时会使用组件渲染器来渲染低代码组件。这样组件在页面设计器中会表现为只是一个组件,而不是内部打算的 schema,用户无法对组件内部直接做修改,只能像使用组件一样通过修改属性来完成对组件的控制,从而实现组件的封装性。
类似的逻辑也可以复用到低代码组件引用低代码组件的场景中。
项目内调试
有些组件因为各种原因在项目内调试效率会更高,如请求域名限制,或者与其他组件联调。因此,我们也提供了低代码组件在低代码项目内调试的能力。
源码组件是通过启动一个本地 server 的形式来和页面进行通信的,但是低代码组件只是一个 web 页面,如何做到类似的效果呢?我们在这个过程中选择了使用 BroadCastChannel 进行 Tab 间通信,当一个低代码组件的设计器被打开时,会告知页面有组件处于调试中。如果页面是后打开的,他也会通过这个通道询问目前是否有正处于调试状态的插件。建立链接后,当组件更新,就可以通过这个通道,告知页面最新的组件 schema,页面拿到后进行重新渲染实现实时调试。
引用其他组件
低代码组件拥有自己的依赖管理能力,可以为每个依赖的组件指定版本和安装构建。在安装其他低代码组件的时候,我们会将组件和被引用组件的依赖进行合并拍平,在这个过程中引导用户进行冲突解决。
组件的版本管理
一个组件应该可以在不同时刻发布不同的版本,应用可以选择性地安装不同版本。低代码组件同样支持发布不同的版本,每个不同版本我们会根据版本号,记录他的 schema,物料描述以及依赖配置。我们还特意设计了一个调试版本 0.1.0,这个版本永远与线上最新的记录同步,方便用户进行应用内的调试。
低代码组件和源码项目的结合
低代码组件作为组件的一种生产方式,不应该限制组件的消费场景。就像源码开发的组件可以在低代码项目中使用一样,低代码生产的组件也应该能在源码中使用。
在我们的实践中,我们使用 LowCodeEngine 的出码模块对组件的 schema 进行出码,转换为 React 代码,同时分析组件的依赖以及物料描述,生成组件所需的 package.json,再补充其他的工程文件。组成一个完整 React 组件脚手架,再利用这个脚手对组件进行构建发布,最终生成一个 npm 包。这个 npm 包其本质就是一个纯粹的 React 组件,因此他可以在任何 React 项目中直接使用。
既然在源码项目中使用,那就会涉及到如何在源码项目中调试。我们不可能在一个迭代进行过程中,每次修改在源码中看到效果都需要进行一次出码,这样的效率是不能接受的。因此我们在项目工程中设置了一个调试插件,通过这个插件,我们可以自由切换组件是否处于调试模式。当处于调试模式时,我们会将组件 alias 到一个通用容器组件上,容器组件会根据包名,找到对应的组件,并从云端拉取最新的 schema 进行调试。实现了不出码,也可以进行低代码组件在源码项目中的调试。
历史记录 & 容灾
在 schema 的存储逻辑上,我们做了多层记录,其中包括以保存时间为维度的云端存储「历史记录」,还有以间隔时间为维度的本地存储「容灾存储」。「历史记录」可以帮助用户方便地切换至某次保存时的 schema,用于应对 hotFix,推倒重来等网络通信没有出现问题时的修改。「容灾存储」则会定时在本地记录用户的 schema,这样当平台的网络出现问题时,用户的修改也不会丢失,只是暂时没有与云端进行同步,可以等到网络恢复时再重新完成同步。
服务化
比起页面,模块在日常的前端开发里更为常见,形式也更为多样,很多逻辑的抽象都可以以一个模块的形式存在。我们也深刻地认识到,不可能通过一个低代码组件平台承担所有的模块类低代码生产诉求。
我们希望可以通过提供一整套的服务化策略,来让业务基于通用版本,定制出符合各自业务特点的低代码模块生产工具。这里面包括了相关的设计器插件,配套的后端服务,开箱即用的管理后台模板,以及用于渲染低代码组件的运行时 sdk。通过这一整套的工具,开发者可以很快速地搭建起自己的低代码组件平台,并且在此之上进行定制。
下图,是我们业务中的一个实践,使用服务化的低代码组件构建了一个钉钉消息卡片的搭建平台。虽然两者在消费和生产链路上都有很大差异,但仍然可以基于同一套底座开发出来。
目前的成果
目前通过低代码的方式,已经产生了超过 3000 个组件,并应用于超过 900 个应用当中。在新增的组件中,有超过 48% 的技术选型选择了低代码组件,在我们的实践中低代码组件已经成为一种重要的技术选型方式。
未来计划
我们初步完成了对于如何通过低代码形式更好地与业务组件想结合的形式,未来我们的方向会向更深更难地领域进军。有三个明确的探索方向:
面向多角色的协同:P2C/D2C/C2D 等与产品、设计实现更高效率的协同 复杂交互能力增强:动效、手势、拖拽等复杂交互场景的封装 组件质量保障:代码规范检查、Code Review 和 自动化测试。
通过这三个方向的进一步完善,希望能够进一步地扩大低代码组件可以承接的业务边界,让模块生产低代码化成为一种新常态。
同时我们开放了一个物料生产体验平台「造物」(https://parts-dev.cn/),通过这样一个平台大家可以实际体验一下本文中提到的物料研发方案,也欢迎大家讨论和交流。同时我们也在持续大力建设低代码开源项目「LowCodeEngine」(https://github.com/alibaba/lowcode-engine/),希望大家多多关注,有问题也欢迎在开源社区与我们进行讨论。
总结
今天我们的讨论到这里就要告一段落了,感谢大家可以看到这里。总结一下今天主要谈论了三个问题,以及其中对应的思考和实践。
物料的现状和挑战:从基础物料到业务物料,低代码带给物料的新的挑战和机会 已有物料和低代码应用:通过分离的物料描述快速接入已有物料,以及通过低代码技术来生产物料。 低代码生产业务物料:低代码技术是如何服务物料生产的,低代码组件和源码组件的关系,以及低代码组件的基础实现原理。
希望这些讨论可以给大家在低代码和业务组件这个交叉领域带来一些灵感或者启发。