目录
- 一、UI设计
- 二、项目分析
- 三、功能分析
- 四、组件分析
- 五、数据结构
- 六、交互流程
- 七、第三方依赖
- 八、关键技术点
- 九、总结
一、UI设计
二、项目分析
项目主要有两部分,一个是可视化编辑器(editor),一个是预览(preview)。使用了lerna包管理多个项目。
1、Editor 编辑器
编辑器可视化工作区域,使用了Preview预览editor模式。
动态表单模块,包括表单项
2、Preview 预览
预览主要承载两个功能,一个是view模式,一个是editor模式。
UI组件库
三、功能分析
头部功能区域包括:
- 模版库 -- 未实现
- 保存 -- 主要保存两份数据,组件数据(pointData)
- 导入/导出 -- pointData
- 复制/粘贴/重做/撤销/删除 -- 操作pointData
- 预览 -- 打开Preview
左侧组件库区域:
- 组件类别 (category),eg:基础类别、多媒体、图表
- 每个类别下面,还包含组件类型 (type),eg:文本、图片、...
中间编辑区域:
-
工作区 -- 可以拖动
-
上刻度、左刻度
-
画布 -- 页面制作白色区域,组件布局
-
控制条 (toolbar)-- 主要是缩放
右侧属性区域:
- 组件属性表单
四、组件分析
1、Editor 容器组件
编辑器布局,上面Header、下面分左边组件库、中间区域工作画布、右边属性表单。布局容器里面用到React DND来处理拖放,Draggable 来处理布局工作画布拖动,缩放。
2、Header 功能组件
功能区,包括保存、导出、导入、预览、复制、粘贴、撤销、重做等。
3、 TabPane 组件库面板
组件库面板,所有组件列表存放的地方。可拖动的组件都放在这个区域。
使用到了第三方库Antd的Tabs。
4、SourceBox 组件
拖拽源容器,所有的组件都需要这个容器包裹,才能具备拖拽能力。实际上组件库渲染的时候已经全部被该容器包裹。
使用了第三方库React DND
5、TargetBox 组件
拖放源容器,接收拖拽源的组件数据,调用ViewRender渲染到画布上。
使用了第三方库React DND
使用了Draggable使整个容器可在工作区域随意拖动。
6、CanvasControl 组件
控制工作画布放大缩小
7、MenuProvider右键菜单组件
为工作画布区域的组件提供右键菜单。使用第三方库react-contexify。
8、Calibration 刻度组件
为工作区域提供尺寸参考。
9、FormRender 动态表单组件
右边属性区域,渲染组件的属性,可编辑。主要基于Antd的Form组件实现。
10、FormItem 表单组件库
包含各种类型的表单组件,图片、文本、颜色、输入框等。
11、ViewRender 渲染组件
预览界面的容器,最外层是一个可拖拽的盒子组件(第三方react-grid-layout组件),里面循环渲染所有的DynamicEngine组件,循环的数据就是PointData。
12、DynamicEngine 组件
这个是实现动态渲染的组件,主要使用Umijs的dynamic动态加载UI组件功能。输入PointDataItem数据,根据类型(type),类别(category)查找到需要渲染的组件目录,动态import组件。
13、UI组件库
组件分不同的类别(category),每个类别下面有不同类型(type)的组件。比如:
- base 基础组件,图片、输入框、文本等
- visual可视化组件,饼图、折线图、柱状图等
- media多媒体,视频、音频等
- 其他自定义的组件
五、数据结构
1、UI组件数据结构 (template, schema)
定义一个UI组件,必须包含template定义、schema定义两部分数据。
- template 组件模板定义,主要是在编辑器组件列表里面显示
{
// 类型
type: 'Footer',
// 网格规则的坐标,主要用在React Grid Layout的布局h属性
h: 10,
// 组件库显示的名称
displayName: '页脚组件',
// 组件类别
category: 'base',
// 网格规则的坐标,主要用在React Grid Layout的布局x属性
x?: 0,
}
- schema 组件schema定义,包含可编辑属性(editData)、props属性配置(config)
// schema 对象
{
// 可编辑属性列表
editData: [ ... ],
// 可编辑属性默认值
config: { ... }
}
// editData item
{
// 属性key,主要设置在css
key: 'bgColor',
// 属性名称,显示在动态表单label
name: '背景色',
// 表单组件类型
type: 'Color',
}
2、布局数据结构(point data)
ViewRender组件主要遍历pointData,调用DynamicEngine来动态渲染组件。这是一个数组,单个项结构如下:
{
id: 'string',
// React Grid Layout网格规则, 布局用
point: { x: number, y: number, w: number, h: number, ... },
// 组件拖拽数据源
item: {
category: string,
type: string,
// 右侧动态表单的数据,主要是组件的外观、布局属性,用的都是style
config: json
...
},
// 布局状态,暂时没搞清除有什么用
status: 'inToCanvas'
}
3、组件拖拽数据源 (source data)
从组件库拖拽一个组件的时候,数据源如下,拖放区域可以接收到这部分数据。
{
// 组件类型,模版配置下面的type
type: template.type,
// 组件默认值
config: schema.config,
// 网格规则的坐标,主要用在React Grid Layout的布局h属性
h: template.h,
// 属性列表
editableEl: schema.editData,
// 组件类型
category: template.category,
// 网格规则的坐标,主要用在React Grid Layout的布局x属性, 很多组件是没有配置这个属性
x: template.x || 0,
}
六、交互流程
1、组件面板初始化
加载组件配置(template) -》map遍历渲染组件,渲染组件用到DynamicEngine,具体见预览流程说明
2、数据处理过程
组件源数据(template,schema)-》经过拖拽封装成拖拽数据源(source)-》Dispatch到状态管理生成pointData
3、可视化设计流程
渲染流程,走预览一样的逻辑。编辑区域交互过程:
左边组件库拖动源组件(Source) -》工作画布(Target目标区域)监听拖放事件,接收到源组件数据 -》调用ViewRender渲染组件到工作画布 -》右边属性区域加载目标组件的属性
- SourceBox 组件用到React DND包装成拖拽源
- TargetBox 组件也用到React DND包装成接收拖放源
- 工作画布监听到PointData,调用ViewRender渲染目标组件
- 数据面板加载当前目标组件的属性
4、组件属性编辑渲染流程(FormRender)
-
从state取到当前组件数据currPoint,读取属性editData表单配置,config默认数据
-
调用FormRender,传入editData,config
-
根据editData的key,name,type,生成对应的表单类型,并初始化默认值
-
监听表单的onFinish事件,获取values,dispatch到state修改得到新的point data
-
ViewRender监听到point data变化,重新渲染可视化设计区域
5、渲染流程
- 输入pointData,pageData
- ViewRender容器组件开始创建布局容器ReactGrid,并根据pageData设置页面外观
- 遍历pointData
- 调用DynamicEngine,输入pointDataItem,动态加载UI组件
- 渲染UI组件到ReactGrid布局容器
- 完成
七、第三方依赖
- Umijs
- Antd
- React DND
- Draggable
- React Grid Layout
- React Contexify
- Redux Undo
八、关键技术点
1、组件设计
目前,这个框架组件设计还相对简单。定义属性、默认值还有组件简单实现。
2、动态表单
动态表单基于Antd的Form实现。
3、拖放功能
拖放功能主要基于React DND实现。
4、动态加载
基于Umijs的Dynamic动态加载组件实现。
5、组件布局
基于React Grid Layout的布局。
九、总结
技术实现不难,简单明了,结构清晰。对于一般的推广活动页面,主要以展示为主,没有太多交互的页面,足够应付了。
附录:
开源项目地址:github.com/MrXujiang/h…
根据自己的使用情况,我对这个开源项目做了一些修改,下面是我的项目地址(仅供参考学习):gitee.com/mokesun/h5-…