【低代碼】1387- 試著換個角度理解低代碼平臺設(shè)計的本質(zhì)

本文會主要分享自己對低代碼平臺的理解,從多個角度和問題去看低代碼平臺的設(shè)計。我覺得「低代碼平臺的核心在于模型設(shè)計,包括控件模型、組件模型、畫布模型等等」。希望看完本文,你能知道:
低代碼平臺核心的底層邏輯是什么? 為何常見低代碼平臺都包含“控件區(qū)”、“布局區(qū)”和“屬性編輯區(qū)”? 低代碼平臺的控件、組件、畫布的本質(zhì)是什么? 如果讓低代碼平臺支持跨平臺? 如何讓低代碼平臺支持自定義數(shù)據(jù)源?
一、你所看見過的低代碼平臺
近幾年國內(nèi)紛紛出現(xiàn)各種低代碼產(chǎn)品,在「降本增效提質(zhì)」方面發(fā)揮重要作用。低代碼平臺的業(yè)務(wù)場景涉及越來越廣泛:自定義表單、頁面制作、活動詳情頁、工作流場景、數(shù)據(jù)報表、大屏數(shù)據(jù)報表、數(shù)據(jù)表格、白板筆記等等。對應(yīng)成熟的低代碼產(chǎn)品也非常多:阿里宜搭[1]、騰訊云搭[2]、百度愛速搭[3]、輕流[4]、Jeecg Boot[5]、碼良[6]等等。
下圖騰訊開源的 tmagic[7] 平臺,是我們最常見的低代碼平臺布局方式:
(本圖來自:tmagic[8] )
其中包括三個核心模塊:
「控件區(qū)」:展示平臺內(nèi)支持的控件,用戶通過拖拽控件到布局區(qū),即可展示控件對應(yīng)的 UI 組件樣式; 「布局區(qū)」:用來承載控件對應(yīng)的 UI 組件,用戶可以對每個 UI 組件進(jìn)行布局,并且直觀查看頁面效果; 「屬性編輯區(qū)」:用來展示該控件支持的配置內(nèi)容,可以更加靈活的對每個控件對應(yīng)的 UI 組件進(jìn)行自定義設(shè)置。
所以,「為何各個產(chǎn)品紛紛采用這類布局?」
二、換個角度思考低代碼平臺設(shè)計
我們在解決問題時,經(jīng)常會使用兩種方法:
「自頂向下法」:從目標(biāo)出發(fā),拆解和細(xì)化問題,找到解決方法; 「自底向上法」:匯總各種零散信息,得到正確方法和結(jié)論。
我們試著用「自頂向下法」思考一下低代碼平臺的設(shè)計:
通常在團(tuán)隊確定是否需要開發(fā)低代碼平臺前,都會通過頭腦風(fēng)暴、靈感討論、業(yè)務(wù)需要情況分析,然后確定開發(fā)低代碼平臺的原始需求。
假設(shè)這么一個場景:
掘金社區(qū)的主頁布局比較單一,當(dāng)需要增加或調(diào)整部分模塊時,需要改動項目代碼、打包、提測、發(fā)布,這時候如果能有一個主頁設(shè)計平臺,讓運營人員自由調(diào)整頁面布局,還可以針對不同節(jié)日、活動調(diào)整出不同主頁布局。
基于這樣的場景,我們使用「自頂向下法」,從目標(biāo)出發(fā),拆解和細(xì)化問題,找出解決方法。
1. 確定目標(biāo)
我們的目標(biāo)需求是能夠靈活的布局社區(qū)主頁:
2. 拆解和細(xì)化問題
如果要實現(xiàn)靈活布局的掘金主頁,就需要將主頁中的模塊抽成每個獨立控件:
如果每個控件需要能夠靈活配置,我們還需要能夠配置控件的任意部分:
3. 找到解決方法
按照前兩個步驟的分析,我們可以確定大致解決方法:
需要實現(xiàn)一個支持自由拖拽布局的設(shè)計平臺; 該平臺支持拖拽不同控件到頁面中; 每個控件支持不同的自定義配置; 設(shè)計器支持導(dǎo)出頁面結(jié)構(gòu),渲染器支持渲染頁面內(nèi)容。
于是我們就有了下面的方案:
這樣是為什么常見低代碼平臺都會有“控件區(qū)”、“布局區(qū)”和“屬性編輯區(qū)”。
通常交互邏輯如下:
從「控件區(qū)」拖拽一個控件進(jìn)入「布局區(qū)」,將控件渲染成對應(yīng)組件; 選中組件,在「屬性配置區(qū)」顯示該組件所有支持配置的屬性; 修改「屬性配置區(qū)」的屬性,更新「布局區(qū)」中該組件的樣式。
這是最簡單的一個流程。
三、思考更加通用的低代碼模型
低代碼平臺創(chuàng)建的頁面,本質(zhì)上不一定是單個頁面,也可以是由多個頁面組成的一個 Web 應(yīng)用,因此,我們可以把上面示例,抽象成更加通用的低代碼平臺模型:
該模型定義了低代碼平臺創(chuàng)建的頁面結(jié)構(gòu),最終的渲染是由對應(yīng)渲染器渲染頁面。
這就有點 VNode 樹的味道啦。
(圖片來源:https://v3.cn.vuejs.org/[9])
對于 Vue 而言,「核心要解決的就是“如何創(chuàng)建 VNode”和“如何渲染 VNode”。」
接下來我們通過 TypeScript 接口形式定義下面的結(jié)構(gòu):
可以發(fā)現(xiàn),單頁應(yīng)用和多頁應(yīng)用的關(guān)系在于,通過為單頁應(yīng)用增加 path配置,將多個單頁應(yīng)用組合成多頁應(yīng)用。
到這里我們就有一個更加通用的低代碼模型,并且使用 TypeScript 接口定義了每一層的結(jié)構(gòu)。
可以看出:「低代碼平臺的核心在于模型設(shè)計,定義每個部分的模型。」
四、控件區(qū)的控件沒這么簡單
1. 控件是什么?
控件本質(zhì)是一個「標(biāo)準(zhǔn)的 JSONSchema 對象,用來描述最終渲染出來的組件」。在低代碼平臺中,將控件拖拽到布局區(qū)才會顯示對應(yīng)的組件樣式。
以「用戶信息控件」為例:
const UserInfo = {
name: '用戶信息控件',
type: 'UserInfoComponent', // 指定渲染的組件名稱
config: [
{
label: '頭像',
type: 'input',
value: 'https://a.com',
},
{
label: '昵稱',
type: 'input',
value: 'pingan8787'
}
]
}
通常我們會在控件對象中定義一個 type(也可能是其他名稱),用來「指定控件所渲染的組件名稱」。比如 Vue 中,就可以通過該 type 值,使用動態(tài)組件 <component :is={type} />形式動態(tài)渲染組件。
控件就好比是組件的說明書,只是對組件進(jìn)行描述,描述了它是什么樣子,有哪些行為、配置等信息。
2. 控件還有什么優(yōu)點?
控件定義成「標(biāo)準(zhǔn)的 JSON 對象」,還有其他優(yōu)點沒比如:「可以實現(xiàn)控件跨平臺適配,在不同平臺/組件庫渲染不同的組件」。目標(biāo)平臺只需按照模型渲染不同組件即可。
3. 控件如何實現(xiàn)動態(tài)加載遠(yuǎn)程組件?
常見的方案是為每個控件指定遠(yuǎn)程組件的地址(如設(shè)置 path 屬性),當(dāng)控件開始被拖拽時,發(fā)送請求獲取遠(yuǎn)程組件:
const UserInfo = {
name: '用戶信息控件',
type: 'UserInfoComponent', // 指定渲染的組件名稱
path: 'https://a.com/UserInfoComponent.js', // 遠(yuǎn)程組件的地址
config: [
{
label: '頭像',
type: 'input',
value: 'https://a.com',
},
{
label: '昵稱',
type: 'input',
value: 'pingan8787'
}
]
}
以 Vue 為例,當(dāng)獲取到遠(yuǎn)程 Vue 組件后,可以通過 Vue 提供的動態(tài)組件進(jìn)行注冊和使用。
完整過程如下:
開始拖拽「控件區(qū)」控件,并發(fā)起請求,從服務(wù)端獲取遠(yuǎn)程組件; 當(dāng)獲取到遠(yuǎn)程組件后,注冊到項目中; 松開控件,渲染組件內(nèi)容到「畫布區(qū)」。
當(dāng)然,考慮到編輯器的性能優(yōu)化,避免每次拖拽都發(fā)送請求獲取組件文件,我們可以這樣優(yōu)化:
使用請求緩存,如果是重復(fù)請求,則從緩存讀取上次請求結(jié)果; 對常用基礎(chǔ)組件預(yù)先發(fā)送請求并保存本地; 本地緩存已請求的組件,下次請求相同組件,則讀取緩存結(jié)果; 等等
五、畫布區(qū)的畫布也沒這么簡單
1. 畫布是什么?
畫布的本質(zhì)也是一個「標(biāo)準(zhǔn) JSON 對象,」它是我們最終要渲染頁面所用的數(shù)據(jù)源,通常包含整個頁面的結(jié)構(gòu)和配置信息。當(dāng)拖拽控件進(jìn)入畫布和更新組件配置時,會更新畫布。
我們根據(jù)掘金主頁,簡單構(gòu)造一個模型(不考慮多頁面情況):
const Juejin = {
title: '掘金主頁',
favicon: './favicon.icon',
components: [
{
name: '用戶信息控件',
type: 'UserInfoComponent',
config: [
{
label: '頭像',
type: 'input',
value: 'https://a.com',
},
{
label: '昵稱',
type: 'input',
value: 'pingan8787'
}
]
}
]
}
在上面模型中,定義了畫布中的每個組件,存放在 components數(shù)組下,每個組件都包含各自的 name、type、config等信息,在渲染器渲染時,就可以:
根據(jù) type渲染配置區(qū)的組件;根據(jù) label渲染配置區(qū)表單的 label 文本;根據(jù) value渲染配置區(qū)表單的值。
2. 畫布還有豐富的配置
對于畫布模型,最重要的應(yīng)該是組件列表,即前面的 components數(shù)組,對于每一個組件,最主要的信息包括:
事件模型信息:包含該組件綁定的一些事件(如事件名稱等); 動畫模型信息:包含該組件綁定的一些動畫效果(如旋轉(zhuǎn)、放大等); UI 樣式模型信息:包含該組件綁定的一些 UI 樣式(如背景色、字號等); 數(shù)據(jù)/數(shù)據(jù)源模型信息:包含該組件綁定的一些數(shù)據(jù)源相關(guān)的配置(如數(shù)據(jù)源接口地址等)。
以「「事件模型信息」」為例,當(dāng)頁面中配置了一個按鈕,這個按鈕往往可以做如下事情:
打開鏈接; 打開彈框; 打開 APP; 刷新頁面; 發(fā)送請求; 等等。
此時,該按鈕可觸發(fā)的行為非常多,如果把每個事件處理邏輯都寫在組件中,會使得組件臃腫無比,且耦合在組件中,可維護(hù)性差。
為了降低組件和事件處理邏輯之間的耦合度,我們可以在組件和事件處理邏輯中間增加一層,即事件總線:
實現(xiàn)通用組件派發(fā)事件到事件總線,不同的業(yè)務(wù)場景監(jiān)聽事件,執(zhí)行具體的事件處理邏輯。
通過事件總線,將派發(fā)事件和監(jiān)聽事件的雙方互相解耦,完成解耦后,還能夠?qū)崿F(xiàn)「跨平臺」的功能,「對于派發(fā)相同的事件,只需要在不同平臺監(jiān)聽該事件,實現(xiàn)不同的處理邏輯即可」。
六、數(shù)據(jù)源設(shè)計
所謂「數(shù)據(jù)源」即低代碼平臺中數(shù)據(jù)來源,通常按照業(yè)務(wù)需求可以將數(shù)據(jù)源分為兩類:
「靜態(tài)數(shù)據(jù)源」:數(shù)據(jù)綁定在頁面配置中,在最終效果頁時,直接使用頁面配置中的數(shù)據(jù),無需通過接口獲取數(shù)據(jù); 「動態(tài)數(shù)據(jù)源」:一般是保存數(shù)據(jù)源的接口在配置中,不綁定數(shù)據(jù),在最終效果頁時,客戶端需要再發(fā)送請求獲取數(shù)據(jù)。
1. 靜態(tài)數(shù)據(jù)源的過程
在低代碼設(shè)計平臺中,平臺先請求數(shù)據(jù),用戶選擇其中指定數(shù)據(jù),保存在頁面配置中。
比如當(dāng)我們已有 banner 列表接口,需要選擇其中一張,添加到布局區(qū)中:
步驟如下:
用戶在「控件區(qū)」選擇「輪播控件」,拖入「布局區(qū)」; 點擊「布局區(qū)」中「輪播控件」的組件,打開「屬性配置區(qū)」; 選擇「屬性配置區(qū)」中「選擇 banner」,平臺發(fā)送請求,從服務(wù)端獲取 banner 列表; 打開「選擇 banner 彈框」,展示 banner 列表,用戶選擇所需 banner 圖片; 點擊「確定」,關(guān)閉「選擇 banner 」彈框,并在「布局區(qū)」的「輪播控件」組件插入該筆數(shù)據(jù),完成選擇。
用戶在「選擇 banner」彈框中,選中指定的數(shù)據(jù),保存到頁面配置中,當(dāng)訪問最終生成效果頁,會直接顯示出已選擇的 banner 圖片。
2. 動態(tài)數(shù)據(jù)源的過程
動態(tài)數(shù)據(jù)源相比靜態(tài)數(shù)據(jù)源,會更加靈活,用戶指定數(shù)據(jù)源接口后,當(dāng)接口數(shù)據(jù)變化,最終效果頁可以動態(tài)改變展示的內(nèi)容。
比如當(dāng)我們已有 banner 列表接口,可以在管理后臺添加不同的 banner,最終效果頁能夠展示新的 banner,而用戶只需在設(shè)計時,指定 banner 列表接口即可:
步驟如下:
用戶在「控件區(qū)」選擇「輪播控件」,拖入「布局區(qū)」; 點擊「布局區(qū)」中「輪播控件」的組件,打開「屬性配置區(qū)」; 選擇「屬性配置區(qū)」中「配置 banner」,配置“接口地址”和“轉(zhuǎn)換規(guī)則”; 選擇完成,點擊「確定」,關(guān)閉「選擇 banner 」彈框,將配置的“接口地址”和“轉(zhuǎn)換規(guī)則”數(shù)據(jù)保存在「布局區(qū)」頁面配置中,配置完成。 當(dāng)用戶訪問最終效果頁時,頁面會先調(diào)用配置的“接口地址”獲取遠(yuǎn)程的 banner 列表; 將接口返回的數(shù)據(jù)通過“轉(zhuǎn)換規(guī)則”,將接口返回的數(shù)據(jù)轉(zhuǎn)換成組件所有的數(shù)據(jù)格式。
這樣就實現(xiàn)了最終效果頁能夠每次都展示最新的數(shù)據(jù),實現(xiàn)完全動態(tài)。
3. 增加數(shù)據(jù)源適配器
當(dāng)需要對兩個耦合度較高的邏輯進(jìn)行解耦,可以通過增加適配器方法進(jìn)行解耦,因此在數(shù)據(jù)源這邊也可以增加適配器對「UI 組件」和「接口數(shù)據(jù)」進(jìn)行解耦。
理想狀態(tài)應(yīng)該是:
UI 組件只對外暴露組件支持的配置和方法,而無需關(guān)注是什么業(yè)務(wù)使用該組件; 接口數(shù)據(jù)也無需關(guān)注數(shù)據(jù)被什么組件使用。
于是,我們分別為「靜態(tài)數(shù)據(jù)源」和「動態(tài)數(shù)據(jù)源」增加了數(shù)據(jù)適配器,流程如下:
靜態(tài)數(shù)據(jù)源
在第 4 步時,接口返回的數(shù)據(jù)會經(jīng)過「數(shù)據(jù)適配器 1」,將接口數(shù)據(jù)轉(zhuǎn)換為「選擇 banner」彈框組件統(tǒng)一的參數(shù)。同理,第 6 步將彈框組件返回的數(shù)據(jù)結(jié)構(gòu),通過「數(shù)據(jù)適配器 2」轉(zhuǎn)換為「banner 組件」所需參數(shù)的數(shù)據(jù)結(jié)構(gòu)。
動態(tài)數(shù)據(jù)源
在第 6 步時,接口返回的數(shù)據(jù)會經(jīng)過「數(shù)據(jù)適配器 」,將接口數(shù)據(jù)轉(zhuǎn)換為「banner 組件」統(tǒng)一的參數(shù)數(shù)據(jù)結(jié)構(gòu)。
其實總結(jié)一下,就是通過各種數(shù)據(jù)適配器,將各種來源的數(shù)據(jù)結(jié)構(gòu)轉(zhuǎn)換為組件的參數(shù)模型即可。好處也很明顯:
更換數(shù)據(jù)源時,只需要按照組件參數(shù)模型對接接口,實現(xiàn)各種數(shù)據(jù)適配器,無需改動原有邏輯; 更換 UI 組件庫時,也只需要按照組件參數(shù)模型對接 UI 組件,實現(xiàn)各種數(shù)據(jù)適配器,無需改動原有邏輯。
4. 總結(jié)數(shù)據(jù)源設(shè)計
按照前面的方案,我們對數(shù)據(jù)源就有了主要方向,其主要的核心在于:通過定義組件接口模型和適配器模型,我們可以很容易的開發(fā)任意組件和適配器,按照定義的模型,其他開發(fā)者也能很方便的開發(fā)。
七、總結(jié)
「低代碼平臺作用在于降本增效提質(zhì),核心在于模型設(shè)計,降低各個功能點的耦合度,讓平臺支持跨平臺」。
本文通過「自頂向下法」,介紹低代碼平臺的設(shè)計思路,「從目標(biāo)出發(fā),拆解和細(xì)化問題,找到解決方法」。后面針對低代碼平臺的幾個核心模塊逐一分析自己的理解,著重介紹了核心模塊的模型設(shè)計和配置。
本文是自己經(jīng)過幾個低代碼平臺實戰(zhàn)后的理解和總結(jié),希望對各位有所幫助,低代碼平臺的未來無限可能。
這是我第一次寫低代碼相關(guān)的文章,如有錯誤,歡迎指正~~
Reference
阿里宜搭: https://yida.alibaba-inc.com/
[2]騰訊云搭: https://cloud.tencent.com/product/weda?ivk_sa=1024320u
[3]百度愛速搭: https://aisuda.baidu.com/
[4]輕流: https://qingflow.com/
[5]Jeecg Boot: http://boot.jeecg.com
[6]碼良: https://godspen.ymm56.com/
[7]tmagic: https://tencent.github.io/tmagic-editor/playground/index.html#/
[8]tmagic: https://tencent.github.io/tmagic-editor/playground/index.html#/
[9]https://v3.cn.vuejs.org/: https://v3.cn.vuejs.org
