低代码之光!轻量级 GUI 的设计与实现

低代码之光!轻量级 GUI 的设计与实现

前言

每当提起低代码,很多人都会下意识的出现过激反应,吐槽低代码都是**,唯恐避之不及。可能大部分人觉得低代码就是替代手写代码,对于程序员来说这是不可接受的。其实低代码表述的含义非常宽泛,我相信很多人可能都在低代码平台中受益过,而且确实可以提升效率。像原型工具(Figma)、建站平台(Webflow、Framer)、BI 报表(Power BI、Looker Studio)、3D 模型搭建(Spline、Womp)、动画编辑器(Rive)等等,这些都是非常有名的一些在线工具。

言归正传,本文并不是为了介绍低代码平台,也不想评价低代码的好坏,只是想聊一聊低代码平台中 GUI 的设计思路和实现方式。

Acrodata GUI 是一款适用于低代码平台的轻量级 GUI 库,现已开源。

GitHub: https://github.com/acrodata/gui
Playground: https://acrodata.github.io/gui/playground

什么是 GUI

GUI 翻译为图形用户界面,是指采用图形方式显示的计算机操作用户界面。在前端编程中,我们一般很少使用 GUI 这样的描述,所以很多人会错误地认为 GUI = UI library。

那么到底什么是 GUI 呢?为了便于理解,我们可以参照前端项目中比较有名的 GUI 项目 dat.gui。做过 3D 可视化或者熟悉 ThreeJS 的朋友一定非常熟悉这个库。dat.gui 的主要用途就是将配置项转换成图形化控件,方便调试参数。

低代码之光!轻量级 GUI 的设计与实现

除了 dat.gui 之外,还有其它几款 GUI 项目也做得不错,tweakpane、lil-gui、leva。

低代码平台中的配置栏

对于使用过低代码平台或者开发过类似产品的朋友来说,低代码平台的布局已经司空见惯,在布局的右侧通常都是配置栏。当然,我们使用的很多软件也是如此。随便贴几张主流工具的截图。

低代码之光!轻量级 GUI 的设计与实现
低代码之光!轻量级 GUI 的设计与实现
低代码之光!轻量级 GUI 的设计与实现

首先说一点,并不是每一款低代码产品都需要 GUI 生成配置,比如第一张截图 Webflow,它的所有组件的配置项都是一样的(全部是 CSS 的可视化配置),这种情况直接写一个公共组件可能更简单。

但是像第三张截图 Looker Studio 这样的产品,每一种图表组件的配置都不一样,同时还允许用户自定义组件,那么这类产品就非常需要一套灵活易用的 GUI 库了。

在 Acrodata GUI 的文档站首页,我用 GUI 创建了一个稍微复杂的 CSS 渐变生成器,它和低代码平台中的配置栏非常类型,欢迎把玩尝试。

低代码之光!轻量级 GUI 的设计与实现

👉 查看 CSS 渐变生成器源码

轻量级 GUI 的设计思路

由于低代码平台的特殊性和复杂性,GUI 库在设计的时候必须要保证能组合出任意数据结构,同时还要简单易用。

基于 JSON 的配置项

为了支持自定义组件,GUI 库更适合采用 JSON 数据进行配置。这和上面提到的 GUI 库在使用上有很大的不同,我们以 dat.gui 和 Acrodata GUI 为例说明。

假设某个组件的配置项如下:

const options = {   content: 'Hello world',   opacity: 0.3,   visible: false, } 

dat.gui 的用法如下:

const gui = new dat.GUI();  gui.add(options, 'content'); gui.add(options, 'opacity', 0, 1).step(0.1); gui.add(options, 'visible'); 

虽然 dat.gui 的用法很简洁,但是这种函数式的声明方式并不适合动态组件,同时也不利于数据保存。

而在 Acrodata GUI 中的用法则是这样的:

<gui-form [config]="config" /> 
const config = {   content: {     type: 'text',     name: 'content',     default: 'Hello world'   },   opacity: {     type: 'slider',     name: 'opacity',     min: 0,     max: 1,     step: 0.1,     default: 0.3   },   visible: {     type: 'switch',     name: 'visible',     default: false   }, } 

上面的 GUI 的配置项和组件的配置项的结构是一样的,只需要将 options 中每个字段的值转换成 UI 控件的 JSON 声明即可。

👉 查看更多基础控件示例

嵌套的数据结构

如果要保证 GUI 可以生成任意数据结构,需要设计五种基础数据(string, number, boolean, object, array)的 JSON 配置项的定义格式。上面的例子中已经展示了三种基本数据类型的定义方式

在 Acrodata GUI 中 object 的定义如下:

{   "size": {     "type": "group",     "name": "Size",     "children": {       "width": {         "name": "Width",         "type": "number",         "default": 1920,         "suffix": "px"       },       "height": {         "name": "Height",         "type": "number",         "default": 1080,         "suffix": "px"       }     }   } } 

最后一种数组类型是最复杂也是最繁琐的。常用数组包含基本数据数组和对象数组,同时每种数组还要支持数组项的动态删减。

下面是一个可动态删减的对象数组的定义方式:

{   "series": {     "type": "tabs",     "name": "Series",     "default": [       { "id": 1, "name": "bar" },       { "id": 2, "name": "foo" }     ],     "template": {       "name": "No.<%= i + 1 %>",       "children": {         "id": {           "type": "number",           "name": "ID"         },         "name": {           "type": "text",           "name": "Name"         }       }     }   } } 

如果对象数组的数组项不相同,则必须搭配 tab 类型来定义:

{   "misc": {     "type": "tabs",     "name": "Misc",     "children": [       {         "type": "tab",         "name": "Full Name",         "children": {           "firstName": {             "type": "text",             "name": "First Name",             "default": "James"           },           "lastName": {             "type": "text",             "name": "Last Name",             "default": "Bob"           }         }       },       {         "type": "tab",         "name": "Contact",         "children": {           "phone": {             "type": "text",             "name": "Phone",             "default": "5550100"           }         }       }     ]   } } 

👉 查看更多组合控件示例

有了上述五种基础控件之后,通过嵌套组合就可以生成任意数据结构了。

JSON Schema 的局限性

为什么不使用 JSON Schema 呢? 很多人可能觉得使用 JSON Schema 定义 JSON 数据会更规范也更通用。这种想法是有道理的,但是 JSON Schema 也有一定的局限性。

首先 JSON Schema 只能定义字段的数据类型,但是无法定义字段的 UI 类型,所以部分使用 JSON Schema 的动态表单方案还会加上 UI Schema,比如 react-jsonschema-form。

另外 JSON Schema 的格式较为复杂,组件的配置项与 JSON Schema 的映射关系非常不直观。

基于响应式表单构建 GUI

Acrodata GUI 是基于 Angular 的响应式表单构建的,核心代码只有大约 200 行(查看源码)。

为了方便在模板中遍历数据,首先需要将 GUI config 对象转换成数组,同时使用响应式表单的 registerControl 在 FormGroup 的实例中注册所有表单控件。然后在模板中使用响应式表单的指令 formGroupName、formControlName、formArrayName 绑定不同 type 的控件就可以了。

Acrodata GUI 使用 Angular Material 作为基础组件库,所有样式和组件都是分模块导入,所以不会产生冗余的代码,其它组件库也可以使用。

开始使用 GUI 组件

<gui-form [config]="config" [model]="model" [form]="form" /> 

config 表示 GUI 的 JSON 配置项,不同类型的控件的配置项稍有不同,详见文档。除了使用 default 定义控件的默认值之外,也可以使用 model(表单值,等同于组件的配置项 options)来定义或更新表单的默认值,这得益于 Angular 响应式表单的 patchValue 方法。

如果你需要监听表单的状态或者值变更,可以使用 form 参数,它可以追踪表单的所有状态变化。

form = new FormGroup({});  this.form.valueChanges.subscribe(v =>{...}); this.form.get('opacity').valueChanges.subscribe(v =>{...}); 

总结

虽然上面展示的 GUI 功能很强大,但是 GUI 和动态表单并不能完全划等号,也不是所有的配置项都适合使用 GUI。因为 GUI 的控件类型有限,而且其本身没有复杂的逻辑,所以在低代码平台中要有取舍的使用 GUI 配置。

如果你喜欢 Acrodata GUI 或者有更好的想法,欢迎和我交流!

发表评论

评论已关闭。

相关文章