We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
大家好, 我是徐小夕, 之前一直在分享可视化低代码的一些实践, 围绕 H5-Dooring 零代码搭建平台也输出了很多技术文章, 最近2.7.0 版本也顺利迭代完成, 这里详细分享一下 H5-Dooring 无代码搭建平台技术方案.
可视化低代码
H5-Dooring
2.7.0
两年前我设计了H5-Dooring的第一个开源版本, 之后陆陆续续迭代了两年, github star已达到6.5k+, 也找到了很多志同道合的小伙伴, 一起研发Dooring系的搭建产品, 如:
Dooring
从技术设计和产品规划上, 这几年也总结摸索出了一些经验和实践, 接下来我就和大家一起分享一下H5-Dooring 的技术架构设计与演进.
我们都知道任何低代码或者零代码搭建产品都非常注重底层搭建协议, 这些产品通常会设计一套向上兼容且可扩展的DSL结构, 来实现页面元件的标准化配置, 并支持元件的向上扩展.
低代码
零代码
搭建协议
DSL
元件
上面这张图是我在设计 V6.Dooring 可视化大屏搭建平台的编辑器架构图, 这里的底层搭建协议可以认为是 搭建基础, 也就是我们常说的 “经济基础决定上层建筑”.
V6.Dooring
搭建基础
在设计H5-Dooring 搭建平台前, 我也参考了很多标准化软件数据协议, 给我启发最大的就是 ODATA, 它是微软于2007年发起的开放协议, 主要由以下几部分组成:
ODATA
微软
OData
URL
为了让可视化搭建平台的组件数据标准化且可扩展, 这里我分享一下H5-Dooring的Schema设计.
Schema
Schema 分两部分:
editData 是 组件属性可编辑项的数组, 每一项里面包含了如下字段:
editData
key和name 都可以按照组件属性的语义来定, 这里值得一提的是 type. 不同属性的值类型不同, 所以我们编辑项的 type 也不同, 所有的类型如下:
key
name
type
更详细的介绍可以访问 dooring 开发文档
config 本质上是一个对象, 也就是组件所能暴露出来的属性集合, 和 editData 数组每一项的key 一致, 如下:
config
{ cpName: 'Header', logoText: '', fontSize: 20, color: 'rgba(47,84,235,1)', height: 60, fixTop: false, menuList: [ { id: '1', title: '首页', link: '/' }, { id: '2', title: '产品介绍', link: '/' }, ] }
我们通过以上的设计规范, 就可以轻松制作一个可实时编辑的低代码组件:
可以在Dooring官方文档体验: 低代码组件案例
最开始设计H5-Dooring的时候为了最大限度的降低用户的搭建成本, 我采用了智能网格布局的方式来搭建页面, 用户只需要在二维空间像搭积木一样选择适合的组件就可以快速的制作页面:
这样虽然可以降低用户的搭建难度, 并能满足一部分受众的搭建需求, 比如说简单的官网, 活动页面制作,下面是一个我搭建的比较有代表性的例子:
但是对于平台方, 为了满足更多场景的页面深度制作, 就必须提供不同场景不同行业的组件物料, 这将对研发带来巨大的压力(虽然也一直在添加新组件).
另一方面, 目前上很多H5活动制作平台基本上都采用的自由布局的模式搭建, 好处就是可以最大限度的还原设计稿, 满足更灵活的搭建需求, 缺点就是使用成本比网格布局的模式要高, 还会涉及图层的概念.
自由布局
网格布局
图层
当然综合评估下来, 确实很有必要给一部分用户提供自由布局的模式, 所以在技术层我设计同时兼容网格布局和自由布局的搭建方案. 当用户在搭建时, 可以轻松选择自己适合的搭建模式:
同时为了满足自由布局下组件的层级管理, 我又设计了图层管理面板和图层操作, 来快速的管理页面元素, 当然图层管理面板 对网格布局 也同样有一定积极作用, 比如快捷的操作组件.
图层管理面板
在前面提到了可视化搭建平台的统一搭建协议和搭建模式, 在这两个核心要素完成之后, 我们就很容易的去设计我们的插件系统.
统一搭建协议
搭建模式
从插件系统的本质来看, 核心价值是对页面操作的整个周期里为页面赋能, 而页面的本质是数据(也就是DSL集).
所以只要有标准的数据规范, 我们自定义的插件就可以很轻松的来对页面进行赋能, 类似于各种技术里面的中间件. 下面是一个例子:
中间件
{ "pageConfig": { "allowOverlap": "freedom", "isLogin": false, "bgColor": "rgba(16,20,29,1)", "bgSize": "100%", "title": "H5-Dooring官网" }, "tpl": [ { "id": "276059", "item": { "category": "base", "config": { "cpName": "XButton", "id": "", "bgColor": "rgba(22,40,212,1)", "width": 190, "marginTop": 0, "round": 16, "text": "按钮", "fontSize": 15, "color": "rgba(255,255,255,1)", "animation": "none", "animationTurn": 1, "delay": 0, "interaction": { "type": "link", "title": "", "params": "", "content": "", "height": 300, "width": 300, "okText": "", "cancelText": "", "onOk": "", "btnColor": "rgba(20,54,226,100)" } }, "h": 23, "type": "XButton" }, "point": { "w": 24, "h": 23, "x": 0, "y": 0, "i": "276059", "moved": false, "static": false, "isBounded": true }, "status": "inToCanvas" }, { "id": "260487", "item": { "category": "base", "config": { "cpName": "LongText", "id": "", "text": "我是长文本有一段故事,dooring可视化编辑器无限可能,赶快来体验吧,骚年们,奥利给~", "color": "rgba(60,60,60,1)", "fontSize": 14, "indent": 0, "lineHeight": 1.8, "textAlign": "left", "bgColor": "rgba(255,255,255,0)", "padding": 0, "radius": 0 }, "h": 36, "type": "LongText" }, "point": { "w": 24, "h": 36, "x": 0, "y": 23, "i": "260487", "moved": false, "static": false, "isBounded": true }, "status": "inToCanvas" } ] }
上面是H5-Dooring生成的一个页面DSL结构, 如果我们要对页面元素进行统计分析, 或者实现出码, 国际化, PSD解析转化等功能, 只需要对数据结构进行分析和处理即可.
PSD
所以说在H5-Dooring平台实现自定义的插件还是非常容易的, 也是低代码或者无代码需要重点规划的一个环节.
H5-Dooring平台的组件编辑器主要是对组件属性进行编辑,比如:
当然还有全局的数据源配置. 如下:
数据源配置
同时由于我们的组件数据协议高度统一, 所以如果想扩展属性配置, 也非常容易, 我们只需要按照数据协议添加属性即可:
同理, v6.dooring 也采用相似的架构, 所以我们可以轻松扩展组件的属性:
有关可视化大屏搭建平台的技术实践可以参考我的另一篇文章 从零设计可视化大屏搭建引擎
由于Dooring的技术栈采用React, 并实现了标准的数据协议层, 所以我们可以利用类似 Taro 等跨平台框架实现多端搭建, 对于我们常用的媒介如移动端, Pad和PC端, 目前编辑器也提供了快捷的切换模式:
React
Taro
所以我们可以轻松的实现不同端的搭建, 实现原理本质上是通过切换画布大小, 并同比例更新元素的计量衡.
图层管理模块也是在Dooring支持了自由布局之后才上线的功能. 因为我们页面中组件的数据结构中包含统一的物理信息:
所以我们只需要分析页面的组件集合, 就可以轻松的渲染出页面中的元素图层信息:
有了图层的概念我们其实可以做很多有用的事情, 比如:
后面 Dooring 也会基于图层能力迭代更多提高用户搭建销效率的功能.
在Dooring 的迭代中花了大部分精力在优化用户搭建体验和协议标准化上, 对于组件物料的丰富上, 我也做了一些设计, 最近也发布了一套低代码组件库的原型:
我们可以轻松的像写 React 组件一样来实现低代码组件, 并支持线上实时编辑, 一个基本的例子如下:
import styles from './index.less'; import React, { memo, useState } from 'react'; import { MenuOutlined } from '@ant-design/icons'; import { IHeaderConfig } from './schema'; const Header = memo((props: IHeaderConfig) => { const { cpName, bgColor, logo, logoText, fontSize, color, showMenuBtn, menuColor, height, } = props; const [showMenu, setShowMenu] = useState(false); const toggleMenu = () => { setShowMenu(!showMenu); }; return ( <header className={styles.header} style={{ backgroundColor: bgColor, height: height + 'px' }} > <div className={styles.logo}> <img src={logo && logo[0] && logo[0].url} alt={logoText} /> </div> <div className={styles.title} style={{ fontSize, color }}> {logoText} </div> {showMenuBtn && ( <> <div className={styles.menuIconWrap} onClick={toggleMenu} style={{ color: menuColor, borderColor: menuColor }} > <MenuOutlined /> </div> </> )} </header> ); }); export default Header;
通过这种标准化的方式, 我们可以给 Dooring 平台提供更为丰富的组件物料.
除了基础物料组件之外, 为了从更大粒度提高用户搭建的效率, 我提供了模版功能, 我们可以把重复的区块和可复用的页面保存为模版:
模版
重复的区块
可复用的页面
我们可以在编辑器页面轻松将页面保存为模版, 并自动生成海报封面:
基于网页生成封面的方式也很简单, 我这里采用的是 dom-to-image 这个库, 当然搭建也可以使用html2canvas.
dom-to-image
html2canvas
表单编辑器的实现思路我之前也写过一些分享, 这里和大家再介绍一下核心的一些思路.
1. 静态化配置列表
静态化配置列表是最传统的表单配置方式之一,基本思路就是利用母表来生成配置项,进而实现表单配置。类似于以下方式: 早期的网站配置就是类似于这种呢方案实现的,比如说我们要定制网站的主色,网站某些组件是否可见,是一种比较简单的方式。但是缺点是每增加一个配置属性,都要开发人员重新编写一个字段配置代码,这种方式在表单开发中非常不灵活,而且对代码层有强依赖性,所以只适合做小型配置系统。比如个人网站,简单的自定义表单。
2. 基于json schema的动态表单配置
基于json schema的动态表单配置有两种实现方案, 一种就是支持在线修改json文件从而实现定制化,另一种就是完全无代码操作,但是前提都需要提供一套通用的表单模版。类似于如下案例: 此种方案可以实现基本的表单自治。也是本文主要实现的方案。至于在线编写json文件的方案。笔者之前也也过成熟的方案,具体可以参考:基于jsoneditor二次封装一个可实时预览的json编辑器组件(react版)
3. 支持在线coding的混合式表单设计 支持在线编程的混合式表单设计方案是终极方案,也是目前流行的无代码化平台的思想之一。一方面它提供了基于json schema的动态表单配置, 对于一些强定制的,需要在线设计组件方案的模式,采用在线编程,实时打包成动态组件的方式,最后根据平台的组件约定来实现组件库的方式。如下图所示: 在线代码编辑可以使用react-codemirror2或者 react-monaco-editor插件来实现。至于在线打包,我们用nodejs完全可以实现,笔者在做Dooring项目的在线下载代码时就用到了该方案,感兴趣的可以了解一下。
可视化领域一方面强调的是图形(可视化)的设计,一方面是动态表单。比如说我们想傻瓜式的改变一张图的数据,属性,交互等,我们需要通过表单这一桥梁来实现: 所以我们需要设计一款适合公司产品的“表单引擎”,来动态根据图形组件的类型渲染不同表单配置。这块思想也是表单设计器要解决的问题之一。在下面的文章中我们会详细介绍实现过程。
在实现表单设计器之前,我们先来整理一下思路和需求。在笔者的最初草图中,它长这样: 从草图中我们可以提取到如下任务信息:
我们这里总结了几个常用的表单组件如下:
以上这些基本满足我们的日常开发需求,其次我们还可以开发数据源表单组件,列表组件,比如dooring实现的那样:
dooring
类似的还有颜色面板这些,我们可以更具业务需求自行定制。
在完成表单组件库之后,我们就需要根据配置项动态渲染了。也有两种实现思路,一种就是类似于多条件判断,如下:
{ item.type === 'Number' && <Form.Item label={item.name} name={item.key}> <InputNumber min={1} max={item.range && item.range[1]} step={item.step} /> </Form.Item> } { item.type === 'Text' && <Form.Item label={item.name} name={item.key}> <Input /> </Form.Item> } { item.type === 'TextArea' && <Form.Item label={item.name} name={item.key}> <TextArea rows={4} /> </Form.Item> }
这样做虽然可行,也有很多成熟系统采用该方案,但是一旦表单变多,比如一个页面有几十个甚至上百个表单项,那么我们将渲染m *** n**次(m为表单组件类型数,n为配置项个数)。另一种方式笔者看来是比较优雅的,可以将复杂度降低到O(n),也就是笔者常用的对象法。思路大至如下:将表单组件的类型作为对象的属性,属性值为对应的表单组件,这样遍历的时候只需要对应上对象的具体类型即可。 代码如下:
// 维护表单控件, 提高form渲染性能 const BaseForm = { "Text": (props) => { const { label, placeholder, onChange } = props return <Cell title={label}> <Input type="text" placeholder={placeholder} onChange={onChange} /> </Cell> }, "Number": (props) => { const { label, placeholder, onChange } = props return <Cell title={label}> <Input type="number" placeholder={placeholder} onChange={onChange} /> </Cell> } } // 动态渲染表单 { formData.map((item, i) => { let FormItem = BaseForm[item.type] return <div className={styles.formItem} key={i}> <FormItem {...item} /> </div> }) }
是不是很优雅呢?后期我们只需要在BaseForm里维护表单组件即可,而且还可以基于BaseForm对表单进行包装,实现动态删除,编辑等功能。如下:
包装后的代码如下:
<div> <div className={styles.disClick}><FormItem {...item} /></div> <div className={styles.operationWrap}> <span onClick={handleEditItem}><EditOutlined /></span> <span onClick={handleDelItem}><MinusCircleOutlined /></span> </div> </div>
接下来我们看看表单的全局属性,通过实际分析我们可以知道表单有如下外观:
所以他们因该成为表单设计的通用属性,如下图所示:
以上的表单通过H5-Dooring设计而来。当然我们可以利用它设计更加自定的表单页面。
最后一个比较使用的需求就是api定制,一般公司可能需要将用户的录入数据收集到自己的平台,那么这个时候我们提供一个api表单提交接口积极很有必要了,上面笔者也展示过,实现很简单,就是配置里多一个api的文本框即可。
在H5编辑器Dooring的实现中,我们可以做抽象,每一个页面组件可以看成特定的表单组件,如下图: 我们可以利用dooring的能力对表单平台进行拖拽,样式设计,数据录入等等操作,感兴趣的朋友可以基于Dooring设计思路改造成自己的表单设计平台。
对于数据收集能力, 可以参考我的另一篇文章:
前端如何一键生成多维度数据可视化分析报表
之前 H5-Dooring 是采用 socket 来实现双向通信的, 不同的用户如何想协作搭建, 可以通过 共享的json文件 或者 socket 来实现. 不过最新市面上也出了非常不错的协作方案, 大家也可以参考一下, 这块的功能设计目前我们正在确定方案.
socket
json
目前 Dooring 支持2种出码方式:
以上就是我们需要做的在线实时打包下载代码的工作流,由于nodejs是单线程的,为了不阻塞进程我们可以采用父子进程通信的方式和异步模型来处理复杂耗时任务,为了通知用户任务的完成状况, 我们可以用socket做双向通信。在当前的场景下就是代码编译压缩完成之后,通知给浏览器,以便浏览器显示下载状态弹窗。一共有三种状态:进行中,已完成,失败。对应如下图所示界面: 至于为什么没有出现下载失败的状态,不要问我,问就是没有失败过(完了,找虐了)。
以上就是H5-Dooring实时编译下载的工作流设计,至于线上更多的实际需求,我们也可以参考以上设计去实现,接下来笔者来具体介绍实现过程。
我们要想实现一个自动化工作流, 要考虑的一个关键问题就是任务的执行时机以及以何种方式执行. 因为用户下载代码之前需要等H5页面打包编译压缩完成之后才能下载, 而这个过程需要一定的时间(8-30s), 所以我们可以认定它为一个耗时任务.
当我们使用nodejs作为后台服务器时, 由于nodejs本身是单线程的,所以当用户请求传入nodejs时, nodejs不得不等待这个"耗时任务"完成才能进行其他请求的处理, 这样将会导致页面其他请求需要等待该任务执行结束才能继续进行, 所以为了更好的用户体验和流畅的响应,我们不得不考虑多进程处理. 好在nodejs设计支持子进程, 我们可以把耗时任务放入子进程中来处理,当子进程处理完成之后再通知主进程. 整个流程如下图所示: nodejs有3种创建子进程的方式,这里笔者简单介绍一下fork的方式。使用方式如下:
// child.js function computedTotal(arr, cb) { // 耗时计算任务 } // 与主进程通信 // 监听主进程信号 process.on('message', (msg) => { computedTotal(bigDataArr, (flag) => { // 向主进程发送完成信号 process.send(flag); }) }); // main.js const { fork } = require('child_process'); app.use(async (ctx, next) => { if(ctx.url === '/fetch') { const data = ctx.request.body; // 通知子进程开始执行任务,并传入数据 const res = await createPromisefork('./child.js', data) } // 创建异步线程 function createPromisefork(childUrl, data) { // 加载子进程 const res = fork(childUrl) // 通知子进程开始work data && res.send(data) return new Promise(reslove => { res.on('message', f => { reslove(f) }) }) } await next() })
在H5-Dooring线上打包的工作流中,我们会用到child_process的exec方法,来解析并执行命令行指令。至于父子进程的更多应用,大家可以自行探索。
在上面介绍的dooring工作流中,我们知道为了实现实时打包,我们需要一个H5 Template项目,作为打包的母版,当用户点击下载时,会将页面的json schema数据传给node服务器, node服务器再将json schema进行数据清洗最后生成template.json文件并移动到H5 Template母版中,此时母版拿到数据源并进行打包编译,最后生成可执行文件。
以上的过程很关键, 这里笔者画个大致的流程图: 为了实现以上过程,我们需要两个关键环节:
第一个环节很好实现,我们只需要用nodejs的fs模块生成文件到指定目录即可,这里笔者重点介绍第二个环节的实现。
当我们将json数据生成到H5 Template中之后,就可以进行打包了,但是这个过程需要自动化的去处理,不能像我们之前启动项目一样,手动执行npm start或者yarn start。我们需要程序自动帮我们执行这个命令行指令,笔者在查nodejs API突然发现了child_process的exec方法,可以用来解析指令,这个刚好能实现我们的需求,所以我们开始实现它。代码如下:
import { exec } from 'child_process' const outWorkDir = resolve(__dirname, '../h5_landing') const fid = uuid(8, 16) const cmdStr = `cd ${outWorkDir} && yarn build ${fid}` // ...exec相关代码 const filePath = resolve(__dirname, '../h5_landing/src/assets/config.json') const res = WF(filePath, data) exec(cmdStr, function(err,stdout,stderr){ if(err) { // 错误处理 } else { // 成功处理 } })
以上代码我们不难理解,我们只需要定义好打包的指令字符串(方式和命令行操作几乎一致),然后传入给exec的第一个参数,他就会帮我们解析字符串并执行对应的命令行指令。在执行完成之后,我们可以根据回调函数(第二个参数)里的参数值来判断执行结果。整个过程是异步的,所以我们不用担心阻塞问题,为了实时反馈进度,我们可以用socket来将进度信息推送到浏览器端。
在上面介绍的 exec实现解析并执行命令行指令 中还有一些细节可以优化,比如代码执行进程的反馈,执行状态的反馈。因为我们用的是异步编程,所以请求不会一直等待,如果不采取任何优化措施,用户是不可能知道何时代码打包编译完成, 也不知道代码是否编译失败,所以这个时候会采取几种常用的放案:
很明显使用websocket双向通信会更适合本项目。这里我们直接使用社区比较火的socket.io.由于官网上有很多使用介绍,这里笔者就不一一说明了。我们直接看业务里的代码使用:
// node端 exec(cmdStr, function(err,stdout,stderr){ if(err) { console.log('api error:'+stderr); io.emit('htmlFail', { result: 'error', message: stderr }) } else { io.emit('htmlSuccess', { result: dest, message: stderr }) } }) // 浏览器端 const socket = io(serverUrl); // ...省略其他业务代码 useEffect(() => { socket.on('connect', function(){ console.log('connect') }); socket.on('htmlFail', function(data){ // ... }); socket.on('disconnect', function(e){ console.log('disconnect', e) }); }, [])
这样我们就能实现服务器任务流的状态实时反馈给浏览器端了。
实现前端下载功能其实也很简单,因为用户配置的H5项目包含了各种资源,比如css,js,html,image,所以为了提高下载性能和便捷性我们需要把整个网站打包,生成一个zip文件供用户下载。原理就是使用jszip将目录压缩,然后返回压缩后的路径给到前端,前端采用a标签进行下载。至于如何实现目录遍历压缩和遍历读取目录, 这里笔者就不说了,感兴趣的可以参考笔者其他的nodejs 的文章。
nodejs
跌跌撞撞的迭代了2年多, 目前已经基本可以使用Dooring搭建大部分的场景应用了, 比如:
等等, 后期会扩展更多的场景, 持续迭代, 满足更多用户的深度定制需求.
目前Dooring 已经完成了几个关键性的能力:
后期会从搭建效率和资源生态 这两个维度继续迭代, 比如:
搭建效率
资源生态
如果大家感兴趣, 也欢迎随时和我交流讨论, 探索真正的技术.
The text was updated successfully, but these errors were encountered:
感谢分享,非常棒
Sorry, something went wrong.
分享很用心,非常棒!
No branches or pull requests
大家好, 我是徐小夕, 之前一直在分享
可视化低代码
的一些实践, 围绕H5-Dooring
零代码搭建平台也输出了很多技术文章, 最近2.7.0
版本也顺利迭代完成, 这里详细分享一下H5-Dooring
无代码搭建平台技术方案.Dooring无代码产品技术演进
两年前我设计了
H5-Dooring
的第一个开源版本, 之后陆陆续续迭代了两年, github star已达到6.5k+, 也找到了很多志同道合的小伙伴, 一起研发Dooring
系的搭建产品, 如:从技术设计和产品规划上, 这几年也总结摸索出了一些经验和实践, 接下来我就和大家一起分享一下
H5-Dooring
的技术架构设计与演进.底层搭建协议标准化
我们都知道任何
低代码
或者零代码
搭建产品都非常注重底层搭建协议
, 这些产品通常会设计一套向上兼容且可扩展的DSL
结构, 来实现页面元件
的标准化配置, 并支持元件的向上扩展.上面这张图是我在设计
V6.Dooring
可视化大屏搭建平台的编辑器架构图, 这里的底层搭建协议可以认为是搭建基础
, 也就是我们常说的 “经济基础决定上层建筑”.在设计
H5-Dooring
搭建平台前, 我也参考了很多标准化软件数据协议, 给我启发最大的就是ODATA
, 它是微软
于2007年发起的开放协议, 主要由以下几部分组成:OData
服务中的数据和模型的URL的规则OData
服务的EDM模型的一种XML格式的表现形式OData
请求和响应URL
的巴科斯范式为了让可视化搭建平台的组件数据标准化且可扩展, 这里我分享一下
H5-Dooring
的Schema
设计.Schema 分两部分:
editData 详解
editData
是 组件属性可编辑项的数组, 每一项里面包含了如下字段:key
和name
都可以按照组件属性的语义来定, 这里值得一提的是type
. 不同属性的值类型不同, 所以我们编辑项的type
也不同, 所有的类型如下:更详细的介绍可以访问 dooring 开发文档
config 详解
config
本质上是一个对象, 也就是组件所能暴露出来的属性集合, 和editData
数组每一项的key
一致, 如下:我们通过以上的设计规范, 就可以轻松制作一个可实时编辑的低代码组件:
可以在Dooring官方文档体验: 低代码组件案例
搭建模式多元化
最开始设计
H5-Dooring
的时候为了最大限度的降低用户的搭建成本, 我采用了智能网格布局的方式来搭建页面, 用户只需要在二维空间像搭积木一样选择适合的组件就可以快速的制作页面:这样虽然可以降低用户的搭建难度, 并能满足一部分受众的搭建需求, 比如说简单的官网, 活动页面制作,下面是一个我搭建的比较有代表性的例子:
但是对于平台方, 为了满足更多场景的页面深度制作, 就必须提供不同场景不同行业的组件物料, 这将对研发带来巨大的压力(虽然也一直在添加新组件).
另一方面, 目前上很多H5活动制作平台基本上都采用的
自由布局
的模式搭建, 好处就是可以最大限度的还原设计稿, 满足更灵活的搭建需求, 缺点就是使用成本比网格布局
的模式要高, 还会涉及图层
的概念.当然综合评估下来, 确实很有必要给一部分用户提供自由布局的模式, 所以在技术层我设计同时兼容
网格布局
和自由布局
的搭建方案. 当用户在搭建时, 可以轻松选择自己适合的搭建模式:同时为了满足
自由布局
下组件的层级管理, 我又设计了图层管理面板
和图层操作, 来快速的管理页面元素, 当然图层管理面板
对网格布局
也同样有一定积极作用, 比如快捷的操作组件.可扩展的插件系统
在前面提到了可视化搭建平台的
统一搭建协议
和搭建模式
, 在这两个核心要素完成之后, 我们就很容易的去设计我们的插件系统.从插件系统的本质来看, 核心价值是对页面操作的整个周期里为页面赋能, 而页面的本质是数据(也就是DSL集).
所以只要有标准的数据规范, 我们自定义的插件就可以很轻松的来对页面进行赋能, 类似于各种技术里面的
中间件
. 下面是一个例子:上面是
H5-Dooring
生成的一个页面DSL
结构, 如果我们要对页面元素进行统计分析, 或者实现出码, 国际化,PSD
解析转化等功能, 只需要对数据结构进行分析和处理即可.所以说在
H5-Dooring
平台实现自定义的插件还是非常容易的, 也是低代码或者无代码需要重点规划的一个环节.可扩展的组件编辑器
H5-Dooring
平台的组件编辑器主要是对组件属性进行编辑,比如:当然还有全局的
数据源配置
. 如下:同时由于我们的组件数据协议高度统一, 所以如果想扩展属性配置, 也非常容易, 我们只需要按照数据协议添加属性即可:
同理, v6.dooring 也采用相似的架构, 所以我们可以轻松扩展组件的属性:
有关可视化大屏搭建平台的技术实践可以参考我的另一篇文章 从零设计可视化大屏搭建引擎
多端搭建支持
由于Dooring的技术栈采用
React
, 并实现了标准的数据协议层, 所以我们可以利用类似Taro
等跨平台框架实现多端搭建, 对于我们常用的媒介如移动端, Pad和PC端, 目前编辑器也提供了快捷的切换模式:所以我们可以轻松的实现不同端的搭建, 实现原理本质上是通过切换画布大小, 并同比例更新元素的计量衡.
图层管理, 让设计更高效
图层管理模块也是在
Dooring
支持了自由布局
之后才上线的功能. 因为我们页面中组件的数据结构中包含统一的物理信息:所以我们只需要分析页面的组件集合, 就可以轻松的渲染出页面中的元素图层信息:
有了图层的概念我们其实可以做很多有用的事情, 比如:
后面
Dooring
也会基于图层能力迭代更多提高用户搭建销效率的功能.低代码组件 & 模版生态
在
Dooring
的迭代中花了大部分精力在优化用户搭建体验和协议标准化上, 对于组件物料的丰富上, 我也做了一些设计, 最近也发布了一套低代码组件库的原型:我们可以轻松的像写
React
组件一样来实现低代码组件, 并支持线上实时编辑, 一个基本的例子如下:通过这种标准化的方式, 我们可以给
Dooring
平台提供更为丰富的组件物料.除了基础物料组件之外, 为了从更大粒度提高用户搭建的效率, 我提供了
模版
功能, 我们可以把重复的区块
和可复用的页面
保存为模版:我们可以在编辑器页面轻松将页面保存为模版, 并自动生成海报封面:
基于网页生成封面的方式也很简单, 我这里采用的是
dom-to-image
这个库, 当然搭建也可以使用html2canvas
.表单设计器 & 数据收集分析能力
表单编辑器的实现思路我之前也写过一些分享, 这里和大家再介绍一下核心的一些思路.
动态表单开发的一般思路
1. 静态化配置列表
静态化配置列表是最传统的表单配置方式之一,基本思路就是利用母表来生成配置项,进而实现表单配置。类似于以下方式:
早期的网站配置就是类似于这种呢方案实现的,比如说我们要定制网站的主色,网站某些组件是否可见,是一种比较简单的方式。但是缺点是每增加一个配置属性,都要开发人员重新编写一个字段配置代码,这种方式在表单开发中非常不灵活,而且对代码层有强依赖性,所以只适合做小型配置系统。比如个人网站,简单的自定义表单。
2. 基于json schema的动态表单配置
基于json schema的动态表单配置有两种实现方案, 一种就是支持在线修改json文件从而实现定制化,另一种就是完全无代码操作,但是前提都需要提供一套通用的表单模版。类似于如下案例:
此种方案可以实现基本的表单自治。也是本文主要实现的方案。至于在线编写json文件的方案。笔者之前也也过成熟的方案,具体可以参考:基于jsoneditor二次封装一个可实时预览的json编辑器组件(react版)
3. 支持在线coding的混合式表单设计
支持在线编程的混合式表单设计方案是终极方案,也是目前流行的无代码化平台的思想之一。一方面它提供了基于json schema的动态表单配置, 对于一些强定制的,需要在线设计组件方案的模式,采用在线编程,实时打包成动态组件的方式,最后根据平台的组件约定来实现组件库的方式。如下图所示:
在线代码编辑可以使用react-codemirror2或者 react-monaco-editor插件来实现。至于在线打包,我们用nodejs完全可以实现,笔者在做Dooring项目的在线下载代码时就用到了该方案,感兴趣的可以了解一下。
可视化领域中的表单引擎
可视化领域一方面强调的是图形(可视化)的设计,一方面是动态表单。比如说我们想傻瓜式的改变一张图的数据,属性,交互等,我们需要通过表单这一桥梁来实现:
所以我们需要设计一款适合公司产品的“表单引擎”,来动态根据图形组件的类型渲染不同表单配置。这块思想也是表单设计器要解决的问题之一。在下面的文章中我们会详细介绍实现过程。
从零实现一款动态表单设计器
在实现表单设计器之前,我们先来整理一下思路和需求。在笔者的最初草图中,它长这样:
从草图中我们可以提取到如下任务信息:
我们这里总结了几个常用的表单组件如下:
以上这些基本满足我们的日常开发需求,其次我们还可以开发数据源表单组件,列表组件,比如
dooring
实现的那样:类似的还有颜色面板这些,我们可以更具业务需求自行定制。
在完成表单组件库之后,我们就需要根据配置项动态渲染了。也有两种实现思路,一种就是类似于多条件判断,如下:
这样做虽然可行,也有很多成熟系统采用该方案,但是一旦表单变多,比如一个页面有几十个甚至上百个表单项,那么我们将渲染m *** n**次(m为表单组件类型数,n为配置项个数)。另一种方式笔者看来是比较优雅的,可以将复杂度降低到O(n),也就是笔者常用的对象法。思路大至如下:将表单组件的类型作为对象的属性,属性值为对应的表单组件,这样遍历的时候只需要对应上对象的具体类型即可。
代码如下:
是不是很优雅呢?后期我们只需要在BaseForm里维护表单组件即可,而且还可以基于BaseForm对表单进行包装,实现动态删除,编辑等功能。如下:
包装后的代码如下:
接下来我们看看表单的全局属性,通过实际分析我们可以知道表单有如下外观:
所以他们因该成为表单设计的通用属性,如下图所示:
以上的表单通过H5-Dooring设计而来。当然我们可以利用它设计更加自定的表单页面。
最后一个比较使用的需求就是api定制,一般公司可能需要将用户的录入数据收集到自己的平台,那么这个时候我们提供一个api表单提交接口积极很有必要了,上面笔者也展示过,实现很简单,就是配置里多一个api的文本框即可。
利用H5-Dooring开发一款表单设计平台
在H5编辑器Dooring的实现中,我们可以做抽象,每一个页面组件可以看成特定的表单组件,如下图:
我们可以利用dooring的能力对表单平台进行拖拽,样式设计,数据录入等等操作,感兴趣的朋友可以基于Dooring设计思路改造成自己的表单设计平台。
对于数据收集能力, 可以参考我的另一篇文章:
前端如何一键生成多维度数据可视化分析报表
协同支持
之前
H5-Dooring
是采用socket
来实现双向通信的, 不同的用户如何想协作搭建, 可以通过 共享的json
文件 或者socket
来实现. 不过最新市面上也出了非常不错的协作方案, 大家也可以参考一下, 这块的功能设计目前我们正在确定方案.出码能力
目前
Dooring
支持2种出码方式:以上就是我们需要做的在线实时打包下载代码的工作流,由于nodejs是单线程的,为了不阻塞进程我们可以采用父子进程通信的方式和异步模型来处理复杂耗时任务,为了通知用户任务的完成状况, 我们可以用socket做双向通信。在当前的场景下就是代码编译压缩完成之后,通知给浏览器,以便浏览器显示下载状态弹窗。一共有三种状态:进行中,已完成,失败。对应如下图所示界面:
至于为什么没有出现下载失败的状态,不要问我,问就是没有失败过(完了,找虐了)。
以上就是H5-Dooring实时编译下载的工作流设计,至于线上更多的实际需求,我们也可以参考以上设计去实现,接下来笔者来具体介绍实现过程。
2. nodejs如何使用父子进程
我们要想实现一个自动化工作流, 要考虑的一个关键问题就是任务的执行时机以及以何种方式执行. 因为用户下载代码之前需要等H5页面打包编译压缩完成之后才能下载, 而这个过程需要一定的时间(8-30s), 所以我们可以认定它为一个耗时任务.
当我们使用nodejs作为后台服务器时, 由于nodejs本身是单线程的,所以当用户请求传入nodejs时, nodejs不得不等待这个"耗时任务"完成才能进行其他请求的处理, 这样将会导致页面其他请求需要等待该任务执行结束才能继续进行, 所以为了更好的用户体验和流畅的响应,我们不得不考虑多进程处理. 好在nodejs设计支持子进程, 我们可以把耗时任务放入子进程中来处理,当子进程处理完成之后再通知主进程. 整个流程如下图所示:
nodejs有3种创建子进程的方式,这里笔者简单介绍一下fork的方式。使用方式如下:
在H5-Dooring线上打包的工作流中,我们会用到child_process的exec方法,来解析并执行命令行指令。至于父子进程的更多应用,大家可以自行探索。
3. 使用child_process的exec实现解析并执行命令行指令
在上面介绍的dooring工作流中,我们知道为了实现实时打包,我们需要一个H5 Template项目,作为打包的母版,当用户点击下载时,会将页面的json schema数据传给node服务器, node服务器再将json schema进行数据清洗最后生成template.json文件并移动到H5 Template母版中,此时母版拿到数据源并进行打包编译,最后生成可执行文件。
以上的过程很关键, 这里笔者画个大致的流程图:
为了实现以上过程,我们需要两个关键环节:
第一个环节很好实现,我们只需要用nodejs的fs模块生成文件到指定目录即可,这里笔者重点介绍第二个环节的实现。
当我们将json数据生成到H5 Template中之后,就可以进行打包了,但是这个过程需要自动化的去处理,不能像我们之前启动项目一样,手动执行npm start或者yarn start。我们需要程序自动帮我们执行这个命令行指令,笔者在查nodejs API突然发现了child_process的exec方法,可以用来解析指令,这个刚好能实现我们的需求,所以我们开始实现它。代码如下:
以上代码我们不难理解,我们只需要定义好打包的指令字符串(方式和命令行操作几乎一致),然后传入给exec的第一个参数,他就会帮我们解析字符串并执行对应的命令行指令。在执行完成之后,我们可以根据回调函数(第二个参数)里的参数值来判断执行结果。整个过程是异步的,所以我们不用担心阻塞问题,为了实时反馈进度,我们可以用socket来将进度信息推送到浏览器端。
4. socket.io实现消息实时推送
在上面介绍的 exec实现解析并执行命令行指令 中还有一些细节可以优化,比如代码执行进程的反馈,执行状态的反馈。因为我们用的是异步编程,所以请求不会一直等待,如果不采取任何优化措施,用户是不可能知道何时代码打包编译完成, 也不知道代码是否编译失败,所以这个时候会采取几种常用的放案:
很明显使用websocket双向通信会更适合本项目。这里我们直接使用社区比较火的socket.io.由于官网上有很多使用介绍,这里笔者就不一一说明了。我们直接看业务里的代码使用:
这样我们就能实现服务器任务流的状态实时反馈给浏览器端了。
5. 使用jszip实现服务端压缩文件并支持前端下载zip包
实现前端下载功能其实也很简单,因为用户配置的H5项目包含了各种资源,比如css,js,html,image,所以为了提高下载性能和便捷性我们需要把整个网站打包,生成一个zip文件供用户下载。原理就是使用jszip将目录压缩,然后返回压缩后的路径给到前端,前端采用a标签进行下载。至于如何实现目录遍历压缩和遍历读取目录, 这里笔者就不说了,感兴趣的可以参考笔者其他的
nodejs
的文章。场景化应用落地
跌跌撞撞的迭代了2年多, 目前已经基本可以使用
Dooring
搭建大部分的场景应用了, 比如:等等, 后期会扩展更多的场景, 持续迭代, 满足更多用户的深度定制需求.
后期规划
目前
Dooring
已经完成了几个关键性的能力:后期会从
搭建效率
和资源生态
这两个维度继续迭代, 比如:如果大家感兴趣, 也欢迎随时和我交流讨论, 探索真正的技术.
The text was updated successfully, but these errors were encountered: