低代码平台近年来成为前端领域的热门话题。它极大地降低了应用开发门槛,让非专业开发者也能通过拖拽、配置等方式快速搭建页面和业务流程。本文将以 lowcode-editor
项目为例,详细讲解如何从零开始实现一个基础的低代码编辑器,涵盖项目结构、核心技术选型、数据结构设计、拖拽实现、递归渲染、属性编辑、状态管理、预览与源码查看等关键环节。
一、项目结构与技术栈
1. 项目结构
项目基于 React + TypeScript,目录结构如下:
kotlin
lowcode-editor/
├── src/
│ ├── editor/
│ │ ├── components/ // 编辑器核心组件
│ │ ├── hooks/ // 自定义 hooks
│ │ ├── materials/ // 物料区组件
│ │ ├── stores/ // 状态管理
│ │ ├── index.tsx // 编辑器主入口
│ │ └── interface.ts // 类型定义
│ ├── App.tsx
│ └── main.tsx
├── public/
├── package.json
└── ...
2. 技术栈
- React + TypeScript:主流前端框架,类型安全,组件化开发。
- TailwindCSS:原子化 CSS 框架,用类名来使 css 生效,提升样式开发效率。
- zustand:轻量级状态管理库,简单易用。
- react-dnd:实现拖拽交互的核心库。
- Allotment:实现容器分栏拖动,提升布局灵活性。
- @monaco-editor/react:集成源码编辑器,方便查看和编辑 JSON 数据。
二、低代码编辑器的核心原理
1. 三大区域划分
- 物料区:左侧,展示可拖拽的组件(如按钮、容器等)。
- 画布区:中间,拖拽组件到此区域,实时生成页面结构。
- 属性区:右侧,选中组件后可编辑其属性。
项目演示:
这种布局便于用户直观操作,快速搭建页面。
2. 用 JSON 维护页面结构
低代码编辑器的核心是用一个 JSON 对象来描述页面结构。每次拖拽、编辑属性,都是在操作这个 JSON。如下是典型的数据结构:
typescript
// src/editor/interface.ts
export interface ComponentNode {
id: string;
type: string;
props: Record<string, any>;
children?: ComponentNode[];
}
页面的所有组件都以树状结构存储在 components
数组中,根节点通常是一个容器型组件。
3. 拖拽实现原理
- 物料区组件支持拖拽(react-dnd)。
- 画布区支持放置(useDrop),接收拖拽过来的组件。
- 拖拽成功后,往 JSON 结构中插入新组件。
typescript
// src/editor/hooks/useDropComponent.ts
import { useDrop } from 'react-dnd';
const [{ isOver }, drop] = useDrop({
accept: 'material',
drop: (item) => {
addComponentToJson(item);
},
collect: (monitor) => ({
isOver: monitor.isOver(),
}),
});
4. 递归渲染 JSON 结构
用 React.createElement
递归把 JSON 渲染成真实页面。
typescript
// src/editor/utils/renderComponents.ts
function renderComponents(node: ComponentNode) {
const Comp = componentMap[node.type];
return React.createElement(
Comp,
node.props,
node.children?.map(renderComponents)
);
}
这种递归渲染方式可以灵活支持任意嵌套的组件结构。
5. 属性编辑与响应
- 选中画布区的组件,右侧展示属性编辑框。
- 修改属性时,直接更新 JSON 对应节点的 props。
typescript
// src/editor/components/PropsEditor.tsx
function handlePropChange(key: string, value: any) {
updateComponentProps(selectedId, key, value);
}
属性编辑器通常会根据组件的 setter 配置动态生成表单项,提升通用性。
6. 蒙层与交互体验
- 鼠标悬浮/点击画布组件时,显示蒙层高亮。
- 点击可选中,右侧属性区联动。
- 支持删除组件。
typescript
// src/editor/components/HoverMask.tsx
<div className={`absolute border-2 border-blue-400 pointer-events-none`} style={maskStyle} />
蒙层的实现通常需要获取目标组件的几何属性,动态调整蒙层位置和大小。
三、状态管理:为什么需要两个仓库?
在 src/editor/stores/
目录下,有两个核心状态仓库:
components.tsx
:用于管理整个页面的组件树(即页面结构 JSON),负责组件的增删改查、选中、拖拽等操作。component-config.tsx
:用于管理所有物料组件的配置(如渲染函数、属性 setter、默认属性等),实现组件名与真实组件代码的映射。
为什么要分成两个仓库?
- 关注点分离:组件树仓库只关心页面结构和实例数据,组件配置仓库只关心物料定义和渲染逻辑,互不干扰,便于维护和扩展。
- 灵活扩展:新增物料组件时,只需在配置仓库注册即可,不影响页面结构数据。
- 高效查找与渲染:渲染页面时可通过组件名快速查找对应的渲染函数和属性配置。
- 属性编辑解耦:属性编辑器可根据组件配置仓库的 setter 自动生成表单,无需硬编码。
2. 画布区接收拖拽
画布区通过 useDrop
接收拖拽过来的组件,并将其插入 JSON 结构。
typescript
// src/editor/components/EditArea.tsx
const [, drop] = useDrop({
accept: 'material',
drop: (item) => addComponent(item),
});
return <div ref={drop}>...</div>;
3. 状态管理(zustand)
使用 zustand 管理组件树和选中状态。
typescript
// src/editor/stores/componentsStore.ts
import create from 'zustand';
const useComponentsStore = create((set) => ({
components: [],
addComponent: (comp) => set((state) => ({ ...state, components: [...state.components, comp] })),
// ...
}));
4. 递归渲染
递归渲染 JSON 结构,支持任意嵌套。
typescript
// src/editor/utils/renderComponents.ts
function renderComponents(node) {
// ...见上文
}
5. 属性编辑
属性编辑器根据组件的 setter 配置动态生成表单。
typescript
// src/editor/components/PropsEditor.tsx
<input value={value} onChange={e => handlePropChange(key, e.target.value)} />
6. 蒙层高亮
通过绝对定位实现蒙层高亮,提升用户交互体验。
typescript
// src/editor/components/HoverMask.tsx
<div style={maskStyle} className="absolute border-2 border-blue-400" />
7. 组件树与源码查看
- 组件树:用 antd 的 Tree 组件展示 JSON 结构,便于整体把控页面结构。
- 源码区:用 Monaco Editor 展示 JSON 数据,支持直接编辑。
typescript
// src/editor/components/SourceCode.tsx
<MonacoEditor value={JSON.stringify(json, null, 2)} language="json" />
8. 预览功能
预览模式下递归渲染 JSON,触发真实事件。
typescript
// src/editor/components/Preview.tsx
return <div>{renderComponents(json)}</div>;
编辑模式与预览模式的主要区别在于事件处理和样式控制。
五、完整开发流程
- 初始化项目,配置 React、TypeScript、TailwindCSS。
- 设计 JSON 数据结构,定义组件节点类型。
- 实现物料区,支持组件拖拽。
- 实现画布区,支持接收拖拽并插入组件到 JSON。
- 实现递归渲染,将 JSON 渲染为真实页面。
- 实现属性区,支持动态编辑组件属性。
- 实现组件高亮、选中、删除等交互。
- 实现组件树和源码区,便于结构和数据管理。
- 实现预览功能,区分编辑与预览模式。
- 持续优化交互体验和代码结构。
六、总结
低代码编辑器的核心在于:用 JSON 结构描述页面,拖拽和属性编辑本质上是对 JSON 的增删改查,递归渲染则将 JSON 转化为真实页面。通过合理的数据结构设计、组件配置、状态管理和交互优化,可以实现一个功能完善、易于扩展的低代码平台。两个状态仓库的分离设计,使得页面结构与物料配置解耦,极大提升了系统的灵活性和可维护性。