前言
每當提起低程式碼,很多人都會下意識的出現過激反應,吐槽低程式碼都是**,唯恐避之不及。可能大部分人覺得低程式碼就是替代手寫程式碼,對於程式設計師來說這是不可接受的。其實低程式碼表述的含義非常寬泛,我相信很多人可能都在低程式碼平臺中受益過,而且確實可以提升效率。像原型工具(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 的主要用途就是將配置項轉換成圖形化控制元件,方便除錯引數。
除了 dat.gui 之外,還有其它幾款 GUI 專案也做得不錯,tweakpane、lil-gui、leva。
低程式碼平臺中的配置欄
對於使用過低程式碼平臺或者開發過類似產品的朋友來說,低程式碼平臺的佈局已經司空見慣,在佈局的右側通常都是配置欄。當然,我們使用的很多軟體也是如此。隨便貼幾張主流工具的截圖。
首先說一點,並不是每一款低程式碼產品都需要 GUI 生成配置,比如第一張截圖 Webflow,它的所有元件的配置項都是一樣的(全部是 CSS 的視覺化配置),這種情況直接寫一個公共元件可能更簡單。
但是像第三張截圖 Looker Studio 這樣的產品,每一種圖表元件的配置都不一樣,同時還允許使用者自定義元件,那麼這類產品就非常需要一套靈活易用的 GUI 庫了。
在 Acrodata GUI 的文件站首頁,我用 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 或者有更好的想法,歡迎和我交流!