復(fù)盤!如何設(shè)計可視化搭建平臺的組件商店?
之前一直在做 lowcode 和可視化相關(guān)的項目,也圍繞可視化技術(shù)輸出了一系列技術(shù)文章,今天我繼續(xù)和大家來聊聊可視化平臺相關(guān)的話題——組件商店。這個需求其實很早在我開源 H5-Dooring 之后就有網(wǎng)友提出過 issue ,如下:
正好最近也做了一系列的重構(gòu)和優(yōu)化,覺得是時侯著手組件商店了,所以借此機會和大家詳細(xì)聊聊如何設(shè)計可視化搭建平臺的組件商店。
按照我一向的寫作風(fēng)格,我會在下面列出文章的大綱,以便大家有選擇且高效率的閱讀和學(xué)習(xí):
- 什么是組件商店,為什么要設(shè)計組件商店
- 組件商店工作流設(shè)計
- 組件商店工作流具體方案實現(xiàn)
- 實現(xiàn)在線代碼編輯器
- 組件提交方案設(shè)計
- 組件審批方案設(shè)計
- 組件上架更新方案
- 可視化搭建平臺組件商店總結(jié)及后期規(guī)劃
文章視圖如下,大家在平時在工作中也可以通過思維導(dǎo)圖的方式來整理思路。
什么是組件商店, 為什么要設(shè)計組件商店
“商店”大家也許并不陌生,對用戶而言,其基本用途是滿足正常的消費需求;對供應(yīng)商而言,商店提供了一種銷售自身產(chǎn)品的營銷推廣渠道;對商店自身而言,一方面獲得了客流和利潤,另一方面還能提供自身價值在空間上的延伸。
通過以上的圖例說明,我們很容易想到一個詞——生態(tài)。因為每一個環(huán)節(jié)都在相互促進(jìn),相互影響。那么對于商店這個實體有了具象的認(rèn)知之后,我們再來看看技術(shù)領(lǐng)域的“組件商店”。
對于可視化搭建平臺而言,其中一個核心的環(huán)節(jié)就是組件資產(chǎn)。用戶在設(shè)計搭建頁面時會消費各種各樣的組件,但是對于不同的用戶而言,組件的需求往往是不一樣的,大部分的 lowcode 或者 nocode 平臺都不能很好的解決用戶這些定制化的組件需求,所以為了解決這一問題,組件商店就孕育而生。
我們需要在平臺中提供一種機制,支撐生產(chǎn)者生產(chǎn)組件,定義組件,同時管理者可以對生產(chǎn)者生產(chǎn)的組件進(jìn)行分類管理(比如上架/下架/刪除/排序等)。做過電商系統(tǒng)的朋友可能會發(fā)現(xiàn),它和電商平臺的商品發(fā)布上線是一個邏輯流程:
那么接下來我就帶大家一起設(shè)計一個這樣的流程,供大家參考。
組件商店工作流設(shè)計
根據(jù)上面的介紹和分析,我們要想實現(xiàn)完整的組件商店工作流,需要滿足以下幾點:
- 組件線上編輯(上傳)模塊
- 組件審核模塊
- 組件更新/發(fā)布模塊
- 組件管理(上架/下架/刪除/下載)
有了以上4塊的支持,基本的組件商店就可以 work 了。具體流程如下:
組件商店工作流具體方案實現(xiàn)
在有了具體的規(guī)劃和設(shè)計之后,我們就可以一步步來實現(xiàn)了。
1.實現(xiàn)在線代碼編輯器
在線代碼編輯器目前市面上有幾種成熟的方案,比如:
- react-monaco-editor
- react-codemirror2
- ace | 性能和功能上可以媲美本地代碼編輯器
大家可以選擇以上任意一種方案,這里筆者采用 react-codemirror2 來實現(xiàn)。實現(xiàn)后的效果如下:
由上圖可以看到我們可以在線編寫React,Css,Js 代碼,并且可以配置組件信息。首先我們來安轉(zhuǎn)一下插件:
yarn add react-codemirror2 codemirror2
codemirror 給我們提供了很多語言支持和不同風(fēng)格的代碼主題,我們可以很輕松的制作不同語言的編輯面板。接下來我?guī)Т蠹覍崿F(xiàn)一個React代碼編輯器:
import {UnControlled as CodeMirror} from 'react-codemirror2';
require('codemirror/mode/jsx/jsx');
const react_code = `import styles from './index.less';
import React, { memo, useState } from 'react';
import ReactDOM from 'react-dom';
const MenuCp = memo((props) => {
const { visible, list, bgColor, top } = props;
return ReactDOM.createPortal(
<div className={styles.menuWrap} style={{display: visible ? 'block' : 'none', backgroundColor: bgColor, top: (top + 6) + 'px'}}>
H5-Dooring
</div>,
(document as any).querySelector('.react-grid-layout').parentNode,
)
})`
const ReactEditPanel = () => {
const codeEditorChange = (
_editor: CodeMirror.Editor,
_data: CodeMirror.EditorChange,
value: string,
type: string
) => {
console.log(_data, value, type)
}
return <CodeMirror
value={react_code}
className={styles.codeWrap}
optinotallow={{
mode: 'jsx',
theme: 'material',
lineNumbers: true
}}
notallow={(editor, data, value) => codeEditorChange(editor, data, value, 'react')}
/>
}
以上就實現(xiàn)了一個簡單的React代碼編輯器,是不是很簡單呢? 同理對于 css ,js 代碼編輯器,也是同樣的方式,我們只需要定義 CodeMirror 屬性的mode 為 css ,javascript 即可。我們還可以設(shè)置 theme 來切換到我們喜歡的代碼主題,這里筆者使用的material風(fēng)格。
單純實現(xiàn)在線代碼編輯器還不夠,我們還需要對代碼進(jìn)行處理,保存,對組件進(jìn)行定義,接下來我們就來看看組件是如何提交的。
2.組件提交方案設(shè)計
當(dāng)“ 生產(chǎn)者 ”編寫好組件代碼之后,需要對組件自身進(jìn)行定義。因為可視化平臺組件物料很依賴平臺的組件開發(fā)協(xié)議,我們需要按照平臺的規(guī)范去上傳規(guī)范的自定義組件,這樣平臺才能更好的理解應(yīng)用組件,保持用戶認(rèn)知的一致性。
組件描述信息筆者這里設(shè)計了如下字段:
- 組件名稱 (中文)
- 組件名 (英文,方便存庫)
- 組件分類 (基礎(chǔ),可視化,營銷,媒體等)
- 組件默認(rèn)大小 (寬高)
- 組件圖標(biāo) (方便用戶認(rèn)知,查找)
大家也可以根據(jù)自己的平臺特性來定義和規(guī)范,這樣我們就可以提交一個完整的組件數(shù)據(jù)了。
目前我的做法是將用戶提交的完整的組件數(shù)據(jù)存在庫中,以便審核轉(zhuǎn)化為可視化平臺可以消費的組件,當(dāng)然大家也可以用更智能的方式,自動對組件代碼信息進(jìn)行提取轉(zhuǎn)化,其缺點就是誤差率無法控制,以及無法對組件進(jìn)行準(zhǔn)確的描述。以下是我提交的效果:
組件提交邏輯也很簡單:
form.validateFields().then(values => {
codeRef.current['form'] = {
...values,
icon: values.icon[0].url
};
req.post('/visible/cp/save', codeRef.current).then(res => {
setIsModalVisible(false);
})
}).catch(errorInfo => {
console.log(errorInfo)
setCurTab("4")
})
我們只需要用 antd 的Form 將用戶填寫的數(shù)據(jù)收集起來提交給后臺接口即可。由于我的后臺采用 nodejs 實現(xiàn),上傳接口無非就是保存組件數(shù)據(jù),實現(xiàn)比較簡單,這里就不一一介紹了。對 nodejs 感興趣的朋友可以參考我之前的開源項目從零搭建全棧CMS系統(tǒng)。
3.組件審批方案設(shè)計
組件審批主要由網(wǎng)站管理人員來操作,當(dāng)用戶組件提交成功之后,客戶端會通過消息信令通知管理員,管理員收到消息后會審核組件。那么整個過程也很簡單,我們可以使用 websocket 來實現(xiàn)消息雙向通信,完整流程如下:
我們先來實現(xiàn) socket 通信,這里我就采用市面比較成熟的 socket.io,來簡單打通一個客戶端, 服務(wù)端的雙向通信流程。
首先我們先按照一下依賴:
# 服務(wù)端
yarn add socket.io
# 客戶端
yarn add socket.io-client
對于服務(wù)端,我們需要進(jìn)行如下改寫:
import koa from 'koa';
import http from 'http';
const app = new koa();
const server = http.createServer(app.callback());
const io = require('socket.io')(server);
// 一些業(yè)務(wù)邏輯...
// socket通信
io.on('connection', (socket) => {
console.log('a user connected');
socket.on('component upload success', (msg) => {
// 通知審批
io.emit('component review', msg)
})
});
server.listen(config.serverPort, () => {
console.log(`服務(wù)器地址:xxx.xxx.xxx`)
});
對于客戶端,邏輯如下:
import io from 'socket.io-client'
import { serverUrl } from '@/utils/tool'
const socket = io(serverUrl);
// ... 一些邏輯
useEffect(() => {
socket.on('connect', function(){
console.log('connect')
});
socket.on('htmlWorked', function(data){
const { result, message } = data
if(result !== 'error') {
// ...
}else {
// ...
}
});
socket.on('disconnect', function(e){
console.log('disconnect', e)
});
}, [])
通過以上的實現(xiàn)方式客戶端和服務(wù)端就可以隨時進(jìn)行通信了。接下來我們看看審批的效果:
用戶在 H5-Dooring 平臺提交的組件都會流入管理后臺,管理員可以對組件進(jìn)行審查以及下載源碼等操作。審批同樣會用到 codemirror 這個庫,主要是將存到數(shù)據(jù)庫的字符串代碼進(jìn)行回顯。當(dāng)然還可以實現(xiàn)線上預(yù)覽組件上線后的效果,這塊大家感興趣的可以自己嘗試一下實現(xiàn)方案,原理也很簡單。我們來看看審批的演示:
4. 組件上架更新方案
組件上架的方案也有很多,比如可以手動上架更新,也可以用程序自動化的完成。那么這里我介紹一下我的線上自動化上架的方案,這里還是需要借助上面介紹的 socket.io 和 nodejs。原理大致如下:
完整的流程就是平臺管理員對組件審批通過之后,會自動用 socket 向服務(wù)端發(fā)送審批通過信號,服務(wù)器會對組件數(shù)據(jù)進(jìn)行解析,寫入對應(yīng)類別下的組件庫里(也就是組件入庫),并將組件信息元寫入到編輯器基座,此時為了更新基座拿到最新的效果,需要 nodejs 解析構(gòu)建指令線上打包基座,基座更新完之后會用 socket 發(fā)送消息給 H5-Dooring,通知用戶組件庫已更新,提示用戶是否刷新組件列表,至此,一個完整的組件上架更新流程就完成了。這里我們實現(xiàn)一下如何線上打包編輯器基座。
首先我們需要對 nodejs 的父子進(jìn)程有一定的了解,比如 child_process。我會使用child_process 的 exec 實現(xiàn)解析并執(zhí)行命令行指令,如果大家對 nodejs 的父子進(jìn)程不太熟悉,也可以參考我之前的文章 基于NodeJS從零構(gòu)建線上自動化打包工作流。
因為整個過程需要自動化的去處理,不能像我們之前打包項目一樣,手動執(zhí)行 npm run build 或者 yarn build 。我們需要程序自動幫我們執(zhí)行這個命令行指令,具體方案如下:
import { exec } from 'child_process'
const outWorkDir = resolve(__dirname, '../h5_base_frame')
const fid = uuid(8, 16)
const cmdStr = `cd ${outWorkDir} && yarn build ${fid}`
router.post(api.generalConfig,
auth,
ctx => {
// ...
exec(cmdStr, function(err, stdout, stderr){
if(err) {
console.log('api error:'+stderr);
io.emit('htmlWorked', { result: 'error', message: stderr })
} else {
io.emit('component update success', { result: 'success', message: 'xxxx' })
}
})
// ...
}
);
通過以上的方式,我們就可以線上打包更新我們的項目基座,并植入任何參數(shù),如果需要動態(tài)寫入文件,我們只需要用 fs 模塊處理即可,通過這樣的線上工作流,我們還可以實現(xiàn)更復(fù)雜的邏輯,甚至實現(xiàn)一個小型的前端CI工作流。那么大家如果有更優(yōu)雅更好的方案,也是可以隨時在評論區(qū)交流。
可視化搭建平臺組件商店總結(jié)及后期規(guī)劃
以上所有的設(shè)計方案實現(xiàn)都是基于實際需求來設(shè)計的,當(dāng)然還有很多細(xì)節(jié)需要處理和優(yōu)化,隨著需求和,業(yè)務(wù)的復(fù)雜,后期還會做組件統(tǒng)計,組件數(shù)據(jù)分析,組件監(jiān)控等能力,大家也可以參考以上的設(shè)計方案,設(shè)計自己的組件商店。