<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          使用 React-DnD 打造簡易低代碼平臺

          共 7062字,需瀏覽 15分鐘

           ·

          2022-02-10 15:19

          前言

          2016年起,低代碼概念開始在國內(nèi)興起,當(dāng)年該行業(yè)總共有 10 起融資事件,之后低代碼行業(yè)融資筆數(shù)整體呈上升趨勢,并在2020年增長至14起,其中億元以上融資有13起。

          image.png
          image.png

          從融資輪次分布上看,2016年天使輪、種子輪、A輪和B輪融資占比為50%,而到2020年,其占比則達(dá)到78.6%,相比2016年上升了28.6%。這可以說明低代碼市場整體仍處于發(fā)展初期 。

          2021 年很多公司,不管大小,開始開發(fā)低代碼平臺。低代碼即無需代碼或只需要通過少量代碼,通過“拖拽”的方式即可快速生成應(yīng)用程序。那么對于開發(fā)者而言,我們應(yīng)該如何入手開發(fā)呢?

          “拖拽”實(shí)現(xiàn)

          關(guān)鍵詞就是“拖拽”,其實(shí)“拖拽”的交互方式早在 Jquery 時(shí)代就有,關(guān)于拖拽在前端實(shí)現(xiàn)主要分為 2 種

          1. 是以 jquery-ui[1] 為代表的 draggable 和 ?Droppable,其原理是通過鼠標(biāo)事件 mousedown、mousemove、mouseup 或者 觸摸事件 touchstart、touchmove、touchend,記錄開始位置和結(jié)束位置、以達(dá)到拖拽傳遞數(shù)據(jù)的效果。

          2. 是通過 ?HTML5 Drag and Drop API[2]

          下面是簡單實(shí)現(xiàn)代碼

          <script>
          function?dragstart_handler(ev)?{
          ?//?A將目標(biāo)元素的?id?添加到數(shù)據(jù)傳輸對象
          ?ev.dataTransfer.setData("application/my-app",?ev.target.id);
          ?ev.dataTransfer.effectAllowed?=?"move";
          }
          function?dragover_handler(ev)?{
          ?ev.preventDefault();
          ?ev.dataTransfer.dropEffect?=?"move"
          }
          function?drop_handler(ev)?{
          ?ev.preventDefault();
          ?//?獲取目標(biāo)的?id?并將已移動(dòng)的元素添加到目標(biāo)的?DOM?中
          ?const?data?=?ev.dataTransfer.getData("application/my-app");
          ?ev.target.appendChild(document.getElementById(data));
          }
          script>

          <p?id="p1"?draggable="true"?ondragstart="dragstart_handler(event)">This?element?is?draggable.p>
          <div?id="target"?ondrop="drop_handler(event)"?ondragover="dragover_handler(event)">Drop?Zonediv>

          更高級的功能是:Drop API 還支持直接從系統(tǒng)桌面直接拖拽文件到瀏覽器中,使用 DataTransfer.files [3]實(shí)現(xiàn)拖拽上傳。

          React-dnd

          React DnD[4] 是 React 和 Redux 核心作者 Dan Abramov 創(chuàng)造的一組 React 工具庫,可以幫助您構(gòu)建復(fù)雜的拖放接口,同時(shí)保持組件的解耦性。例如,React DnD 沒有提供一個(gè)排序組件,相反,它為您提供了所需的工具。

          官方 demo

          一起來看下簡單實(shí)現(xiàn)

          react-dnd-demo.gif

          首先需要在項(xiàng)目根節(jié)點(diǎn)設(shè)置拖拽實(shí)現(xiàn)方式

          import?{?render?}?from?'react-dom'
          import?Example?from?'./example'
          import?{?DndProvider?}?from?'react-dnd'
          import?{?HTML5Backend?}?from?'react-dnd-html5-backend'

          function?App()?{
          ????return?(
          ??????<div?className="App">
          ????????<DndProvider?backend={HTML5Backend}>
          ??????????<Example?/>
          ????????DndProvider>

          ??????div>
          ????)
          }

          如果是手機(jī)端就要使用 react-dnd-touch-backend,因?yàn)?react-dnd-html5-backend不支持觸摸。

          DragBox 的實(shí)現(xiàn)

          import?{?useDrag?}?from?'react-dnd';
          import?{?ItemTypes?}?from?'./ItemTypes';
          const?style?=?{
          ????cursor:?'move'
          };
          export?const?Box?=?function?Box({?name?})?{
          ????const?[{?isDragging?},?drag]?=?useDrag(()?=>?({
          ????????type:?ItemTypes.BOX,
          ????????item:?{?name?},
          ????????end:?(item,?monitor)?=>?{
          ????????????const?dropResult?=?monitor.getDropResult();
          ????????????if?(item?&&?dropResult)?{
          ????????????????alert(`You?dropped?${item.name}?into?${dropResult.name}!`);
          ????????????}
          ????????},
          ????????collect:?(monitor)?=>?({
          ????????????isDragging:?monitor.isDragging(),
          ????????????handlerId:?monitor.getHandlerId(),
          ????????}),
          ????}));
          ????const?opacity?=?isDragging???0.4?:?1;
          ????return?(<div?ref={drag}?style={{?...style,?opacity?}}>{name}div>);
          };
          • 這里的 type 就是一個(gè)字符串,用于約束“拖”和“放”組件的關(guān)系,如果字符串不一致就無法回調(diào)事件,主要是為了避免頁面中多個(gè)拖放的實(shí)例
          • item 就是拖動(dòng)時(shí)候傳遞的數(shù)據(jù)
          • end 是拖放結(jié)束后的回調(diào)
          • collect 用于獲得拖動(dòng)的狀態(tài),可以設(shè)置樣式

          DropContainer 實(shí)現(xiàn)

          import?{?useDrop?}?from?'react-dnd';
          import?{?ItemTypes?}?from?'./ItemTypes';

          const?style?=?{
          ????...
          };
          export?const?DropContainer?=?()?=>?{
          ????const?[{?canDrop,?isOver?},?drop]?=?useDrop(()?=>?({
          ????????accept:?ItemTypes.BOX,
          ????????drop:?()?=>?({?name:?'Dustbin'?}),
          ????????collect:?(monitor)?=>?({
          ????????????isOver:?monitor.isOver(),
          ????????????canDrop:?monitor.canDrop(),
          ????????}),
          ????}));
          ????const?isActive?=?canDrop?&&?isOver;
          ????let?backgroundColor?=?'#222';
          ????if?(isActive)?{
          ????????backgroundColor?=?'darkgreen';
          ????}
          ????else?if?(canDrop)?{
          ????????backgroundColor?=?'darkkhaki';
          ????}
          ????return?(<div?ref={drop}?role={'Dustbin'}?style={{?...style,?backgroundColor?}}>
          ???{isActive???'Release?to?drop'?:?'Drag?a?box?here'}
          ????????div>
          );
          };
          • type 與拖動(dòng)的 type 相同
          • drop 函數(shù)返回放置節(jié)點(diǎn)的數(shù)據(jù),返回?cái)?shù)據(jù)給 drag end
          • collect 用于獲得拖動(dòng)狀態(tài)的狀態(tài),可以設(shè)置樣式

          低代碼實(shí)現(xiàn)

          回到我們的低代碼主題,我們來一起看下釘釘宜搭的頁面設(shè)計(jì)

          宜搭.png

          主要分為3個(gè)區(qū)域:左側(cè)組件區(qū)、中間設(shè)計(jì)區(qū)、右側(cè)編輯區(qū)。如果只看左側(cè)組件區(qū)和中間的設(shè)計(jì)區(qū)是否跟 react-dnd 官方的 demo 很相似呢?

          定義 JSON

          接下來我們要:

          • 定義可拖動(dòng)的組件類型
          • 每個(gè)組件類型對應(yīng)的渲染組件
          • 每個(gè)組件的屬性設(shè)置

          先來定義幾個(gè)可拖動(dòng)的字段吧,比如最基本的數(shù)據(jù)類型,div、h1、 p 標(biāo)簽都是一個(gè)組件,那就我先定義出以下字段類型,

          const?fields=?[
          ??{
          ????type:?'div',
          ????props:?{
          ??????className:?'',
          ????},
          ??},
          ??{
          ????type:?'h1',
          ????props:?{
          ??????className:?'text-3xl',
          ??????children:?'H1',
          ????},
          ??},

          ??{
          ????type:?'p',
          ????props:?{
          ??????className:?'',
          ??????children:?'段落111',
          ????},
          ??}
          ??...
          ]

          針對這些拖動(dòng)字段,需要有渲染的組件,而針對div、h1、 p 這些就是標(biāo)簽本身,但是我們需要用 react 封裝成組件

          const?previewFields?=?{
          ??div:?(props:?any)?=>?<div?{...props}?/>,
          ??h1:?(props:?any)?=>?<h1?{...props}?/>,
          ??p:?(props:?any)?=>?<p?{...props}?/>,
          ??...
          }

          右側(cè)邊界區(qū)域的可配置字段

          const?editAreaFields?=?{
          ????div:?[
          ??????{
          ????????key:?'className',
          ????????name:?'樣式',
          ????????type:?'Text',
          ??????},
          ????],
          ????h1:?[
          ??????{
          ????????key:?'children',
          ????????name:?'內(nèi)容',
          ????????type:?'Text',
          ??????},
          ????],
          ????p:?[
          ??????{
          ????????key:?'children',
          ????????name:?'內(nèi)容',
          ????????type:?'Text',
          ??????},
          ??????{
          ????????key:?'className',
          ????????name:?'樣式',
          ????????type:?'Text',
          ??????},
          ????],
          ????...
          }

          上述字段代表 div 只能設(shè)置 className、h1 只能設(shè)置內(nèi)容、p 標(biāo)簽既能設(shè)置內(nèi)容,也可以設(shè)置 className。右側(cè)區(qū)域的也可以配置不同的組件,比如 Text 就渲染成最簡單的 Input。

          嵌套拖動(dòng)

          基本組件一般可以嵌套的,比如我現(xiàn)在想要拖動(dòng)出下圖的頁面效果

          image.png

          實(shí)際上我需要生成 JSON 樹,然后根據(jù) JSON 樹渲染出頁面。

          image.png

          當(dāng)每次拖動(dòng)的時(shí)候,可以生成一個(gè) uuid,然后使用深度優(yōu)先遍歷樹數(shù)據(jù)從根節(jié)點(diǎn)到葉子節(jié)點(diǎn)的由上至下的深度優(yōu)先遍歷樹數(shù)據(jù)。在放置的組件,然后操作數(shù)據(jù)

          export?const?traverse?=?extends?{?children?:?T[]?}>(
          ??data:?T,
          ??fn:?(param:?T)?=>?boolean
          )?=>?{
          ??if?(fn(data)?===?false)?{
          ????return?false
          ??}

          ??if?(data?&&?data.children)?{
          ????for?(let?i?=?data.children.length?-?1;?i?>=?0;?i--)?{
          ??????if?(!traverse(data.children[i],?fn))?return?false
          ????}
          ??}
          ??return?true
          }

          豐富組件

          還可以使用開源組件,集成到低代碼中,我們只需要定義右側(cè)編輯區(qū)域和左側(cè)字段數(shù)據(jù),比如現(xiàn)在集成 @ant-design/charts[5]

          以柱狀圖為例,我們定義下拖動(dòng)的字段數(shù)據(jù)

          {
          type:?'Column',
          module:?'@ant-design/charts',
          h:?102,
          displayName:?'柱狀圖組件',
          props:?{
          ??xField:?'name',
          ??yField:?'value',
          ??data:?[
          ????{
          ??????name:?'A',
          ??????value:?20,
          ????},
          ????{
          ??????name:?'B',
          ??????value:?60,
          ????},
          ????{
          ??????name:?'C',
          ??????value:?20,
          ????},
          ??],
          },

          渲染 直接可以使用import { Column } from '@ant-design/charts';

          props 增加默認(rèn)數(shù)據(jù)就可以直接渲染出漂亮的柱狀圖了。

          image.png

          然后增加一個(gè)數(shù)據(jù)編輯的組件,最后的效果如下圖

          image.png

          生成代碼

          有了 JSON 樹,就可以生成想要的視圖代碼。組件類型 + props + 子組件的數(shù)據(jù), 每個(gè)節(jié)點(diǎn)的代碼就是這段字符串拼接而成。

          <${sub.type}${props}>${children}

          而 props 也可以拼接成 key=value 的形式。遍歷數(shù)據(jù)要 從葉子節(jié)點(diǎn)到根節(jié)點(diǎn)的由下而上的深度優(yōu)先遍歷樹數(shù)據(jù)。

          代碼格式化

          我們可以使用 prettier 來格式化代碼,下面代碼是將格式化代碼的邏輯放到一個(gè) webWork 中。

          importScripts('https://unpkg.com/[email protected]/standalone.js');
          importScripts('https://unpkg.com/[email protected]/parser-babel.js');

          self.addEventListener(
          ??'message',
          ??function?(e)?{
          ????self.postMessage(
          ??????prettier.format(e.data,?{
          ????????parser:?'babel',
          ????????plugins:?prettierPlugins,
          ??????})
          ????);
          ??},
          ??false
          );

          預(yù)覽

          代碼有了,接下來就可以渲染頁面進(jìn)行預(yù)覽了,對于預(yù)覽,顯然是使用iframeiframe除了src屬性外,HTML5還新增了一個(gè)屬性srcdoc,用來渲染一段HTML代碼到iframe

          iframeRef.value.contentWindow.document.write(htmlStr)

          效果

          拖拽一個(gè)表格 和一個(gè)柱狀圖

          image.png

          查看代碼

          image.png

          最后附上 github 和預(yù)覽地址

          • ?? 倉庫地址:?github.com[6]
          • ?? 預(yù)覽地址:?low-code.runjs.cool[7]

          小結(jié)

          本地記錄一個(gè)簡易低代碼的實(shí)現(xiàn)方式,簡單概括為 拖拽 -> JSON Tree——> 頁面

          但想要真正生產(chǎn)可用還有很長的路要走,比如

          • 組件數(shù)據(jù)綁定和聯(lián)動(dòng)
          • 隨著組件數(shù)量的增加需要將組件服務(wù)化,動(dòng)態(tài)部署
          • 組件開發(fā)者的成本與維護(hù)者的上手成本權(quán)衡
          • 組件模板化
          • 頁面部署投產(chǎn)等

          以上任意一點(diǎn)都可能投入較高的成本,個(gè)人認(rèn)為目前低代碼,成本比較低且可以投產(chǎn)的方式有

          1、類似?mall-cook[8] H5搭建

          image.png

          2、類似 json-editor[9] 表單搭建

          image.png

          ?本文對低代碼搭建的思考和討論可能還不夠完整, 歡迎討論和補(bǔ)充。?希望這篇文章對大家有所幫助,也可以參考我往期的文章或者在評論區(qū)交流你的想法和心得,歡迎一起探索前端。

          參考資料

          [1]

          jquery-ui: https://jqueryui.com/draggable/

          [2]

          HTML5 Drag and Drop API: https://developer.mozilla.org/en-US/docs/Web/API/HTML_Drag_and_Drop_API

          [3]

          DataTransfer.files : https://developer.mozilla.org/zh-CN/docs/Web/API/DataTransfer/files

          [4]

          React DnD: https://react-dnd.github.io/react-dnd/about

          [5]

          @ant-design/charts: https://charts.ant.design/zh/docs/manual/getting-started

          [6]

          github.com: https://github.com/maqi1520/react-antd-low-code

          [7]

          low-code.runjs.cool: https://low-code.runjs.cool/

          [8]

          mall-cook: https://github.com/wangyuan389/mall-cook

          [9]

          json-editor: https://github.com/json-editor/json-editor


          The End

          • 點(diǎn)個(gè)?「在看」,讓更多的人也能看到這篇文章;

          • 關(guān)注公眾號?「前端Sharing」?,我們持續(xù)分享 Javascript 熱門框架和前端工程師進(jìn)階內(nèi)容;


          瀏覽 210
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  免费 69视频 | 色哟哟免费视频一区二区三区 | 自拍青青在线视频 | 国产视频久久 豆花 | 亚洲av片日韩av片 |