前端:從零開發(fā)一款可視化搭建框架(前端可視化框架有哪些)

去年上線的可視化編輯器 H5-dooring 至今已有一年的時間,期間有很多熱心的網(wǎng)友和大佬提出了非常多寶貴的建議,我們也在一步步實現(xiàn)中,以下是幾個比較典型的低代碼可視化平臺需求:

  • 出碼能力(即源碼下載功能)
  • 組件交互(即組件支持業(yè)務(wù)中常用的鏈接跳轉(zhuǎn),彈窗交互,自定義事件等)
  • 數(shù)據(jù)源管理(即用戶創(chuàng)建的不同頁面擁有共享數(shù)據(jù)的能力,不同組件之間也有共享數(shù)據(jù)的能力)
  • 組件商店(即用戶可以自主生產(chǎn)組件,定義組件,接入組件數(shù)據(jù)的能力)
  • 布局能力(即用戶可以選擇不同的布局方案來設(shè)計頁面)
  • 常用功能集成(頁面截圖,微信分享,debug能力)

上面的這些功需求已經(jīng)在 H5-dooring 陸續(xù)實現(xiàn)了,在我之前的文章中也有對應(yīng)的技術(shù)分享。但是為了讓更多的人能低成本的擁有自己的可視化搭建系統(tǒng),我們團隊的大佬花了非常多的時間研究和沉淀,最近也開源了一款可視化搭建框架 dooringx-lib,我們可以基于它輕松制作可視化編輯器,而無需考慮內(nèi)部的實現(xiàn)細節(jié),接下來我就和大家分享一下這款可視化框架的使用方式和實現(xiàn)思路,同時也非常感謝 dooring可視化團隊 各位大佬們的辛勤付出。

前端:從零開發(fā)一款可視化搭建框架(前端可視化框架有哪些)

dooringx

可視化搭建框架基本使用和技術(shù)實現(xiàn)

為了讓大家更好的理解可視化搭建框架,我這里舉幾個形象的例子:

  1. antd —— antd-pro

前端:從零開發(fā)一款可視化搭建框架(前端可視化框架有哪些)

我們都知道 antd 是流行的前端組件庫,那么基于它上層封裝的管理后臺 antd-pro 就是它的上層應(yīng)用。

  1. GrapesJS —— craft.js

前端:從零開發(fā)一款可視化搭建框架(前端可視化框架有哪些)

GrapesJS 是一款國外的頁面編輯器框架(詳細介紹可參考我之前的文章 這款國外開源框架, 讓你輕松構(gòu)建自己的頁面編輯器) ,那么 craft.js 就是它的上層應(yīng)用框架。

  1. dooringx-lib —— dooringx

前端:從零開發(fā)一款可視化搭建框架(前端可視化框架有哪些)

dooringx

dooringx-lib 是一款可視化搭建框架,同理 dooringx 就是基于 dooringx-lib 的可視化編輯器。

之所以要介紹它們的區(qū)別,是因為之前有很多朋友對這塊概念理解的不是很清晰,在了解了可視化搭建框架 的 “內(nèi)涵” 之后,我們開始今天的核心內(nèi)容。

1.技術(shù)棧

在分享框架實現(xiàn)思路之前當(dāng)然要自報家門,框架實現(xiàn)上我們還是采用熟悉的 React 生態(tài),移動端組件庫采用的眾安團隊的 zarm,編輯器應(yīng)用層采用的 antd,至于其他的比如拖拽參考線,狀態(tài)管理插件機制等都是我們團隊大佬自研的方案。如果你是 vue 或者其他技術(shù)棧為主的團隊,也可以參考實現(xiàn)思路,相信也會對你有一定的啟發(fā)。

2.基本使用方式

在開始深入之前我們先看看如何使用這款框架,我們只需要按照如下方式即可安裝使用:

npm/yarn install dooringx-lib

同時我們還提供了基礎(chǔ)的使用demo,方便大家在自己的工程中快速上手:

# 克隆項目# cnpmjsgit clone https://github.com.cnpmjs.org/H5-Dooring/dooringx.git# orgit clone https://github.com/H5-Dooring/dooringx.git# 進入項目目錄cd dooringx# 安裝依賴yarn install# 啟動基礎(chǔ)示例yarn start:example# 啟動 dooringx-libyarn start# 啟動 dooringx doc 文檔yarn start:docyarn build

demogithub 項目如下:

前端:從零開發(fā)一款可視化搭建框架(前端可視化框架有哪些)

github地址: https://github.com/H5-Dooring/dooringx

在了解完使用方式之后,我們來看看基本架構(gòu)和實現(xiàn)思路。

3.dooringx-lib基礎(chǔ)架構(gòu)和工作機制

前端:從零開發(fā)一款可視化搭建框架(前端可視化框架有哪些)

上圖就是我根據(jù)目前 dooringx-lib 的項目架構(gòu)梳理的架構(gòu)圖,基本包含了搭建化編輯框架的大部分必備模塊。為了保證框架的靈活性,我們還可以按需安裝對應(yīng)的功能組件,開發(fā)自定義的組件等。如下是一個基本的導(dǎo)入案例:

import { RightConfig, Container, useStoreState, innerContainerDragUp, LeftConfig, ContainerWrapper, Control,} from 'dooringx-lib';

我們將整個框架拆分成了不同的模塊,這些模塊既相互獨立又可以相互關(guān)聯(lián)。完整的工作流程如下:

前端:從零開發(fā)一款可視化搭建框架(前端可視化框架有哪些)

由上圖可以看出,我們只需要擁有基礎(chǔ)的業(yè)務(wù)研發(fā)能力,就可以借助 dooringx-lib 構(gòu)建一個屬于自己的搭建平臺,就好比任何程序的本質(zhì): 數(shù)據(jù)邏輯。

4.dooringx-lib插件開發(fā)

接下來我會和大家分享 dooringx-lib 的插件開發(fā)方式和具體實現(xiàn)(如何導(dǎo)入插件,如何編寫組件,如何注冊函數(shù)等),如果大家感興趣的話也可以跟著下面的方式實踐一下。

4.1 如何導(dǎo)入組件

前端:從零開發(fā)一款可視化搭建框架(前端可視化框架有哪些)

我們在上圖可以看到左側(cè)是我們的組件物料區(qū),分為基礎(chǔ)組件媒體組件,可視化組件,它們的添加會統(tǒng)一放在 LeftRegistMap 數(shù)組中來管理,其基本結(jié)構(gòu)如下:

const LeftRegistMap: LeftRegistComponentMapItem[] = [ { type: 'basic', // 組件類別 component: 'button', // 組件名稱 img: 'icon-anniu', // 組件icon displayName: '按鈕', // 組件中文名 urlFn: () => import('./registComponents/button'), // 注冊回調(diào) },];

左側(cè)組件支持同步導(dǎo)入或者異步導(dǎo)入。

如果需要異步導(dǎo)入組件,則需要填寫 urlFn,需要一個返回 promise 的函數(shù)。也可以支持遠程載入組件,只要 webpack 配上即可。

如果需要同步導(dǎo)入組件,則需要將組件放入配置項的 initComponentCache 中,這樣在載入時便會注冊進 componentRegister 里。

initComponentCache: { modalMask: { component: MmodalMask }, },

4.2 如何定制左側(cè)面板

前端:從零開發(fā)一款可視化搭建框架(前端可視化框架有哪些)

左側(cè)面板傳入 leftRenderListCategory 即可。

leftRenderListCategory: [ {type: 'basic',icon: <HighlightOutlined />,displayName: '基礎(chǔ)組件', }, {type: 'xxc',icon: <ContainerOutlined />,custom: true,customRender: <div>我是自定義渲染</div>, },],

type 是分類,左側(cè)組件顯示在哪個分類由該字段決定。icon 則是左側(cè)分類小圖標(biāo)(如上圖所示)。當(dāng) customtrue 時,可以使用 customRender 自定義渲染。

4.3 開發(fā)一個自定義的可視化組件

組件需要導(dǎo)出一個由 ComponentItemFactory 生成的對象:

const MButton = new ComponentItemFactory( 'button', '按鈕', {style: [ createPannelOptions<FormMap, 'input'>('input', { receive: 'text', label: '文字', }),],animate: [createPannelOptions<FormMap, 'animateControl'>('animateControl', {})],actions: [createPannelOptions<FormMap, 'actionButton'>('actionButton', {})], }, {props: { ... text:'x.dooring'// input配置項組件接收的初始值}, }, (data, context, store, config) => {return <ButtonTemp data={data} store={store} context={context} config={config}></ButtonTemp>; }, true);export default MButton;

其中第一個參數(shù)為組件注冊名,第二個參數(shù)用來展示使用。

第三個參數(shù)用來配置右側(cè)面板的配置項組件。其中鍵為右側(cè)面板的分類,值為配置項組件數(shù)組。

第四個參數(shù)會配置組件的初始值,特別注意的是,制作組件必須要有初始寬度高度(非由內(nèi)容撐開),否則會在適配時全選時產(chǎn)生問題。

這個初始值里有很多有用的屬性,比如fixed代表使用固定定位,可以結(jié)合配置項更改該值,使得組件可以fixed定位。

還有 canDrag 類似于鎖定命令,鎖定的元素不可拖拽。

初始值里的 rotate 需要個對象,value 代表旋轉(zhuǎn)角度,canRotate 代表是否可以操作旋轉(zhuǎn)。(0.7.0版本開始支持)

第五個參數(shù)是個函數(shù),你將獲得配置項中的 receive 屬性(暫且都默認(rèn)該配置為receive)傳來的配置,比如上例中 receive 的是 text,則該函數(shù)中 data 里會收到該字段。

context 一般只有 previewedit,用來進行環(huán)境判斷。

config 可以拿到所有數(shù)據(jù),用來制作事件時使用。

第六個參數(shù) resize 是為了判斷是否能進行縮放,當(dāng)為 false 時,無法進行縮放。

第七個參數(shù) needPosition,某些組件移入畫布后會默認(rèn)采取拖拽的落點,該配置項默認(rèn)為 true, 就是需要拖拽的位置,為 false 時將使用組件自身 topleft 定位來放置。

4.4 事件注冊

前端:從零開發(fā)一款可視化搭建框架(前端可視化框架有哪些)

  1. 注冊時機

事件可以細分為 注冊時機函數(shù),組件內(nèi)可以通過 hook 的方式來實現(xiàn)注冊時機:

useDynamicAddEventCenter(pr, `${pr.data.id}-init`, '初始渲染時機'); //注冊名必須帶id 約定!useDynamicAddEventCenter(pr, `${pr.data.id}-click`, '點擊執(zhí)行時機');

useDynamicAddEventCenter 第一個參數(shù)是 render 的四個參數(shù)組成的對象。第二個參數(shù)是注冊的時機名,必須跟 id 相關(guān),這是約定,否則多個組件可能會導(dǎo)致名稱沖突,并且方便查找該時機。

注冊完時機后,我們需要將時機放入對應(yīng)的觸發(fā)位置上,比如這個 button 的點擊執(zhí)行時機就放到 onclick 中:

<Button onClick={() => {eventCenter.runEventQueue(`${pr.data.id}-click`, pr.config); }}> x.dooring</Button>

其中第一個參數(shù)則為注冊的時機名,第二個為 render 函數(shù)中最后一個參數(shù) config

  1. 函數(shù)注冊

函數(shù)由組件拋出,可以加載到事件鏈上。比如,注冊個改變文本函數(shù),那么我可以在任意組件的時機中去調(diào)用該函數(shù),從而觸發(fā)該組件改變文本。

函數(shù)注冊需要放入 useEffect 中,在組件卸載時需要卸載函數(shù)!否則會導(dǎo)致函數(shù)越來越多。

useEffect(() => {const functionCenter = eventCenter.getFunctionCenter();const unregist = functionCenter.register( `${pr.data.id} 改變文本函數(shù)`, async (ctx, next, config, args, _eventList, iname) => { const userSelect = iname.data; const ctxVal = changeUserValue( userSelect['改變文本數(shù)據(jù)源'], args, '_changeval', config, ctx ); const text = ctxVal[0]; setText(text); next(); }, [ { name: '改變文本數(shù)據(jù)源', data: ['ctx', 'input', 'dataSource'], options: { receive: '_changeval', multi: false, }, }, ]);return () => { unregist();};}, []);

函數(shù)中參數(shù)與配置見后面的函數(shù)開發(fā)。

4.5 右側(cè)面板開發(fā)

前端:從零開發(fā)一款可視化搭建框架(前端可視化框架有哪些)

為了開發(fā)自定義的右側(cè)屬性面板,我們只要將開發(fā)的組件配成一個對象放入 initFormComponents 即可。為了良好的開發(fā)體驗,需要定義個 formMap 類型:

export interface FormBaseType { receive?: string;}export interface FormInputType extends FormBaseType { label: string;}export interface FormActionButtonType {}export interface FormAnimateControlType {}export interface FormMap { input: FormInputType; actionButton: FormActionButtonType; animateControl: FormAnimateControlType;}

formMap 的鍵名就是 initFormComponents 鍵名,formMap 的值對應(yīng)組件需要收到的值。

input 組件為例,FormInputType 此時有2個屬性: label, receive

那么在開發(fā)該組件時,props 會收到:

interface MInputProps { data: CreateOptionsRes<FormMap, 'input'>; current: IBlockType; config: UserConfig;}

也就是 dataformMap 類型,而 current 是當(dāng)前點擊的組件,config 就不用說了。

還記得在左側(cè)組件開發(fā)中的第三個參數(shù)嗎?這樣就都關(guān)聯(lián)起來了:

style: [ createPannelOptions<FormMap, 'input'>('input', { receive: 'text', label: '文字' })],

createPannelOptions 這個函數(shù)的泛型里填入對應(yīng)的組件,將會給收到的配置項良好的提示。

在配置項組件里所要做的就是接收組件傳來的配置項,然后去修改 current 的屬性:

function MInput(props: MInputProps) { const option = useMemo(() => {return props.data?.option || {}; }, [props.data]); return (<Row style={{ padding: '10px 20px' }}> <Col span={6} style={{ lineHeight: '30px' }}> {(option as any)?.label || '文字'}: </Col> <Col span={18}> <Input value={props.current.props[(option as any).receive] || ''} onChange={(e) => { const receive = (option as any).receive; const clonedata = deepCopy(store.getData()); const newblock = clonedata.block.map((v: IBlockType) => { if (v.id === props.current.id) { v.props[receive] = e.target.value; } return v; }); store.setData({ ...clonedata, block: [...newblock] }); }} ></Input> </Col></Row> );}

由于可以很輕松的拿到 store,所以可以在任意地方進行修改數(shù)據(jù)。

將組件的 value 關(guān)聯(lián) current 的屬性,onChange 去修改 store,這樣就完成了個雙向綁定。

注意:如果你的右側(cè)組件需要用到 block 以外的屬性,可能需要去判斷是否處于彈窗模式。

4.6 自定義右鍵菜單

前端:從零開發(fā)一款可視化搭建框架(前端可視化框架有哪些)

右鍵菜單可以進行自定義:

// 自定義右鍵const contextMenuState = config.getContextMenuState();const unmountContextMenu = contextMenuState.unmountContextMenu;const commander = config.getCommanderRegister();const ContextMenu = () => { const handleclick = () => {unmountContextMenu(); }; const forceUpdate = useState(0)[1]; contextMenuState.forceUpdate = () => {forceUpdate((pre) => pre 1); }; return (<div style={{ left: contextMenuState.left, top: contextMenuState.top, position: 'fixed', background: 'rgb(24, 23, 23)', }}> <div style={{ width: '100%' }} onClick={() => { commander.exec('redo'); handleclick(); }} > <Button>自定義</Button> </div></div> );};contextMenuState.contextMenu = <ContextMenu></ContextMenu>;

先拿到 contextMenuStatecontextMenuState 上有個 unmountContextMenu 是關(guān)閉右鍵菜單方法。所以在點擊后需要調(diào)用關(guān)閉。同時上面的 lefttop 是右鍵的位置。另外,我們還需要在組件內(nèi)增加強刷,賦值給 forceUpdate,用于在組件移動時進行跟隨。

4.7 表單驗證提交思路

前端:從零開發(fā)一款可視化搭建框架(前端可視化框架有哪些)

表單驗證提交有非常多的做法,因為數(shù)據(jù)全部是聯(lián)通的,或者直接寫個表單組件也可以。在不使用表單組件時,簡單的做法是為每個輸入組件做個驗證函數(shù)與提交函數(shù)。這樣是否驗證就取決于用戶的選取,而拋出的輸入可以讓用戶選擇放到哪,并由用戶去命名變量。

在點擊提交按鈕時,調(diào)用所有組件的驗證函數(shù)與提交函數(shù),使其拋給上下文,再通過上下文聚合函數(shù)聚合成對象,最后可以通過發(fā)送函數(shù)發(fā)送給對應(yīng)后端,從而完成整個流程。我們可以在 dooringx 中試下這個demo。

另一種方式是可以專門寫個提交按鈕,固定了參數(shù),以及部分規(guī)則,比如規(guī)定在頁面中的所有表單都會被收集提交。

那么我們可以利用數(shù)據(jù)源,將所有表單輸出內(nèi)容自動提交給數(shù)據(jù)源,最后的提交按鈕按數(shù)據(jù)源規(guī)定格式的key 提取,發(fā)送給后端。

后期規(guī)劃

后期我們還會在產(chǎn)品功能方面持續(xù)迭代優(yōu)化,如果大家有好的建議, 也可以隨時和我們交流, 也歡迎在 github 上積極提 issue。

如果大家對可視化搭建或者低代碼/零代碼感興趣,也可以參考我往期的文章或者在評論區(qū)交流你的想法和心得,歡迎一起探索前端真正的技術(shù)。

github: dooringx | 快速高效搭建可視化拖拽平臺
團隊:Dooring可視化團隊

更多推薦

  • 如何設(shè)計可視化搭建平臺的組件商店?
  • 從零設(shè)計可視化大屏搭建引擎
  • 從零使用electron搭建桌面端可視化編輯器Dooring
  • (低代碼)可視化搭建平臺數(shù)據(jù)源設(shè)計剖析
  • 從零搭建一款PC頁面編輯器PC-Dooring
  • 如何搭積木式的快速開發(fā)H5頁面?

相關(guān)新聞

聯(lián)系我們
聯(lián)系我們
公眾號
公眾號
在線咨詢
分享本頁
返回頂部