前言

此低代码编辑器 demo,实现用意是让用户可以通过拖拉拽的形式,自行配置页面展示。

再做一下区分,由于业务性,不涉及到生成结构性代码生成,而是生成描述形式的 JSON,交由负责展示的项目或者模块翻译表达。

产品需求确认

我列出来的这些都需要和产品确认,如果有的话,没有产品,我们在开发前也需要明确:

二级页面、滚动页拖拽、悬浮重叠组件、组件内交互、参数量级、校验。

明确我们要做的大致内容,以及面临的挑战,这样在开发中才不会迷失方向。

接下来就是调研工作,低代码产品,不是一个新东西,很多公司都做过,我们可以取取经。

这里我一笔带过了~

技术路线/技术实现

实现一个 demo,效果为左侧拖拽组件,中间预览,右侧编辑 form。

左侧拖拽至右侧

左侧主要是组件的展示预览区域,一般会简要告诉用户该组件是做什么的,用户可以通过点击或拖拽添加至中间预览区编辑。其中涉及相对困难的点在于:

左侧拖拽至右侧,实现方案:

基于原生 drag 实现,没有找到合适的第三方支持

  • 由于左侧本身应该不支持拖拽排序,而只是允许拖拽至右侧,并且组件并不会消失,这与多拖拽组件设计之初相违背,使用原生则会简单可拓展一些。

涉及难点:拖拽至中间时,存在多个组件,显示正确的预放置 drophere 站位符位置。

解决思路:由于每个 dragitem 都会有一个唯一值,在拖拽进入 dragOverItem 时可以获取到对应的 id,从而显示占位符。

预览主体

这里需要区分一下需要 y 轴排列拖拽滚动的长页面,和需要叠层任意拖拽的海报 h5 类的页面。

y 轴排列拖拽滚动的长页面:

主体拖拽实现,可以采用 @dnd-kit/core 或者 react-beautiful-dnd,我这里用的是 dnd-kit 对比 react-beautiful-dnd 暂时没有性能上的差距。

遇到的一些小 bug 的记录:

  • 拖拽遮挡

问题原因:是未设置拖拽时 zindex 更高

  • 非一致高度时,replace 高度继承 hoverItem 高度,导致拖动一个高度大的组件到高度小的组件位置时,空出的高度是小的,不容易看出来。

问题原因:CSS.Transform.toString(transform)会在移动元素时应用缩放到 X 和 Y 轴的变换。相反,使用 transform: CSS.Translate.toString(transform)可以解决这个问题。这是因为 CSS.Translate.toString(transform)会应用平移变换,而不会应用缩放变换。这样可以确保在拖动元素时不会出现缩放问题。

叠层任意拖拽的 h5 类的页面

主体拖拽实现,使用的第三方库 react-rnd,支持 resize 和 drag 控制。

主要功能实现在于其 onDragStop 时更新对应 id 的 x 与 y 值,onResizeStop 时更新 width 和 height。

还需优化的点:

前置功能需要完成多语言后台配置,而非后端配置。需要重新制定一套多语言方案。

右侧 form

可以对组件的样式, 逻辑, 交互进行配置,设计上主要在于数据存储和数据校验。

在数据结构设计和数据流这一块,单独放在后续文章中详细讲解。使用的是 zustand 存储数据,由 store 统一处理数据。

校验设计,简要概括:

  1. submit 提交时循环 value 触发对应的 validate 回调
  2. 找到所有校验不通过的 form,记录 id 和 cKey(组件唯一键)
  3. 切换到对应的组件 form
  4. 触发 form 的 onInit 触发 validateFields 保证 form 正确标红

待优化的点:

实际编写时,比较麻烦的是需要写两套校验,1 是 form 中的 rules,2 是 from 对应的校验回调函数,考虑是否可以合二为一。

产品设计上的优化,应保存奖品库或商品库,用时只需要添加并判断有无添加即可,而不需要判断其中的各种复杂的校验。

顶部功能区

功能包括:放回,页面名称,保存,预览(手机预览,需要校验数据),调试用 json 看板。

预览支持需要注意:需要验证数据,走保存逻辑,由后端生成的预览链接进行显示。

其他

公共组件库 OR 独立开发

公共组件库

  • 优势:
    • 单一源头
    • 减少重复工作
    • 方便进行组件的更新和维护
  • 劣势:
    • 维护成本高
    • 开发成本高,需要考虑更多维情况
    • 相对增加更多的代码体积
    • 相对增加页面加载速度

独立开发,与公共组件库优劣相反。

从开发成本与以用户优先角度,最终还是选择*独立开发 *的方式。

性能考虑

  • Dashboard 编辑处理速度
    • 资源占用过高: 可视化开发平台通常提供了大量的可视化组件和功能,这些组件和功能可能会消耗大量的内存和计算资源。如果应用程序在内存高的情况下运行,可能会导致系统资源不足,从而影响平台的运行速度或导致假死。
    • 复杂性和层次嵌套: 低代码平台通常支持复杂的应用程序开发,这可能导致应用程序的层次结构复杂,组件之间的嵌套层次深。当应用程序的层次嵌套非常深时,平台需要处理大量的组件和数据关系,这可能导致性能下降和假死现象。
    • 数据量过大: 如果应用程序处理大量的数据,例如大型数据集或复杂的数据操作,这可能会占用大量的内存和计算资源。在内存高的情况下,系统可能无法有效地处理这些大量的数据,导致性能下降或假死。
  • 可能的解决方案
  • 优化资源使用: 确保应用程序和平台在设计和实现时充分优化资源的使用,避免资源的浪费和不必要的占用。
  • 简化层次结构: 尽量简化应用程序的层次结构和组件嵌套,减少复杂性,提高性能。
  • 处理大数据量: 对于大数据量的情况,采取适当的数据处理策略,如分页加载、服务器端数据处理等,以减轻客户端的资源压力。
  • H5 渲染速度
    • 图片资源压缩
    • 动态组件加载

组件描述文件 json⭐

Item 数据类型:

{
id: string;
cKey: CKey;
title: string;
formValue: FormValueType<CKey>;
}
{
pageId: string //增加:记录所在页面
state: { width?: string, height?: string, x: string, y: string } //增加:记录长宽&位置
id: string
cKey: CKey
title: string
formValue: FormValueType<CKey>
}

store:

type State = {
items: Item[]
activeItem: Item | undefined
errorIds: { id: string; cKey: CKey }[]
//...
}

type Actions = {
setActiveItem: (item: Item | undefined) => void
changeFormValues: (value: any) => void
setErrorIds: (item: { id: string; cKey: CKey }) => void
//...
}
// from 初始数据和对应类型
export const defaultFormValue: { [K in CKey]: FormValueMap[K] } = {...}
// 左侧图文列表
export const cItems: { icon: ReactNode; cKey: CKey; title: string }[] = [...]
//...

梳理字段

header 头:

  • pageId:唯一
  • pageName:用于标识编辑页面

左侧组件列需要数据:

  • iconName: 用于图标显示,无需存元素
  • title:描述组件标题
  • cKey:唯一,通过 key 确定指向其对应组件

主体预览部分数据: key-value:cKey-component

  • id:唯一,后端 id 或使用 uuid 生成的 id 值,唯一确定组件,一定要有不然排序和性能会有问题。
  • cKey:组件对应 key 值,可能不唯一,添加相同组件。
  • formValue:表单数据(实时改变)
  • className:样式数据(实时改变)

配置区数据: key-value:cKey-from

  • title(可能需要)
  • initFormValue:表单初始数据
  • initClassName:初始样式数据

类型定义

要求能规范到 key 和对应的 formValue,实例参考:

type CKey = "CKEY1" | "CKEY2" | "CKEY3"

type FormValueMap = {
CKEY1: { title: string }
CKEY2: number
CKEY3: boolean
}

type FormData<K extends CKey> = {
id: string
cKey: K
formValue: FormValueMap[K]
className: string
}

const formData: FormData<"CKEY1"> = {
id: "1",
cKey: "CKEY1",
formValue: { title: "Hello World" },
}