第二章 编辑器技术选型:前端框架对比与拖拽库选择

好,咱们进入正题。这一章聊的是技术选型,说白了就是「用什么造轮子」。我见过不少团队,一上来就开干,结果做到一半发现框架不合适,拖拽库性能拉胯,状态管理乱成一锅粥……嗯,这种坑我踩过不止一次。

所以这一章,我会把前端框架、拖拽库、状态管理这三块掰开揉碎讲清楚。你想想看,选对了工具,后面开发效率能翻倍;选错了,光重构就能让你怀疑人生。

2.1 前端框架对比:React vs Vue vs Angular

先聊框架。低代码平台的核心是「组件化」和「响应式」,这两点决定了我们选型的方向。

维度 React Vue Angular
学习曲线 中等(JSX + Hooks) 低(模板语法直观) 高(TypeScript + 依赖注入)
组件化 函数组件 + Hooks,灵活 单文件组件,清晰 模块化 + 装饰器,严谨
响应式 手动触发(setState) 自动追踪(Proxy) Zone.js 自动检测
生态 最丰富,拖拽库多 丰富,但拖拽库偏少 官方全家桶,但拖拽库少
性能 虚拟 DOM + Fiber,优秀 虚拟 DOM + 编译优化,优秀 真实 DOM + 变更检测,中等
社区活跃度 最高 高(国内尤其) 中等

我个人习惯用 React。为什么?因为低代码编辑器最核心的交互就是「拖拽」和「状态同步」,React 的 Hooks 机制让这两个场景写起来特别顺手。举个例子,你拖一个按钮到画布上,需要实时更新组件树和属性面板——用 React 的 useState + useCallback 就能优雅搞定。

Vue 也不错,尤其是它的响应式系统。我在项目中遇到过用 Vue 做编辑器的团队,他们利用 reactivecomputed 自动追踪组件状态变化,写起来确实省心。但问题是,Vue 的拖拽库生态不如 React 丰富,很多高级功能得自己封装。

至于 Angular……嗯,说实话,我不推荐。它的强类型和依赖注入适合大型企业应用,但低代码编辑器需要频繁操作 DOM 和实时反馈,Angular 的变更检测机制反而成了负担。我曾经试过用 Angular 做原型,结果一个拖拽操作触发了一整棵组件树的检测,卡得不行。

我的建议:如果你团队 React 经验多,选 React;如果更看重上手速度,选 Vue。Angular 就跳过吧,除非你们团队全是 Angular 老手。

2.2 拖拽库选择:react-dnd vs dnd-kit

框架定下来,接下来就是拖拽库。这是编辑器的灵魂,选不好后面全是坑。

市面上主流的 React 拖拽库就两个:react-dnddnd-kit。我两个都用过,说说真实感受。

2.2.1 react-dnd

老牌库,功能强大,但学习曲线陡。它基于 HTML5 拖拽 API,提供了 DragSourceDropTarget 两个高阶组件。用起来大概是这样的:

import { useDrag } from 'react-dnd';

function DraggableButton() {
  const [{ isDragging }, drag] = useDrag(() => ({
    type: 'COMPONENT',
    item: { type: 'Button', props: { text: '点击' } },
    collect: (monitor) => ({
      isDragging: monitor.isDragging(),
    }),
  }));

  return (
    <div ref={drag} style={{ opacity: isDragging ? 0.5 : 1 }}>
      拖我
    </div>
  );
}

看着还行对吧?但实际用起来,你会发现几个问题:

  • 类型定义繁琐:每个拖拽项都要定义 type,还要手动管理 collect 函数。
  • 性能问题:拖拽过程中,整个组件树都会重新渲染,得自己加 React.memo 优化。
  • 触摸设备支持差:默认不支持移动端,得额外引入 react-dnd-touch-backend

我在第一个编辑器项目里用的就是 react-dnd,结果被坑惨了。拖拽时页面卡顿,还得手动处理滚动容器和嵌套放置区的冲突。嗯,后来我果断换了。

2.2.2 dnd-kit

dnd-kit 是后起之秀,设计理念更现代。它用 useDraggableuseDroppable 两个 Hook,API 简洁很多:

import { useDraggable } from '@dnd-kit/core';

function DraggableButton() {
  const { attributes, listeners, setNodeRef, transform } = useDraggable({
    id: 'button-1',
    data: { type: 'Button', props: { text: '点击' } },
  });

  const style = transform ? {
    transform: `translate3d(${transform.x}px, ${transform.y}px, 0)`,
  } : undefined;

  return (
    <button ref={setNodeRef} style={style} {...listeners} {...attributes}>
      拖我
    </button>
  );
}

你看,代码量少了一半。而且 dnd-kit 有几个杀手级特性:

  • 自动性能优化:默认只更新拖拽中的元素,不会触发全量渲染。
  • 内置碰撞检测:支持矩形、圆形、指针位置等多种碰撞算法。
  • 无障碍支持:默认支持键盘拖拽和屏幕阅读器。
  • 移动端友好:触摸事件开箱即用。

小技巧:dnd-kit 的 closestCenter 碰撞算法特别适合低代码编辑器的「插入到两个组件之间」场景。我后来做编辑器时,就用它实现了类似 Figma 的自动吸附效果。

所以我的结论很明确:新项目直接上 dnd-kit。除非你维护的是老项目,否则别碰 react-dnd。

2.3 状态管理方案

拖拽搞定了,状态管理又是个大头。低代码编辑器的状态有多复杂?你想想看:

  • 组件树(嵌套结构)
  • 每个组件的属性(可能几十个字段)
  • 选中状态、悬停状态、拖拽状态
  • 历史记录(撤销/重做)
  • 页面配置(主题、尺寸、断点)

这么多状态,如果全用 useState 硬扛,代码会变成一坨屎山。我见过一个项目,光状态变量就定义了 30 多个,改一个地方得同步五六个 setState……

常见的方案有三种:

方案 适用场景 优点 缺点
React Context + useReducer 中小型编辑器 无额外依赖,简单 性能差(Context 更新会触发所有消费者)
Zustand 中大型编辑器 轻量、API 简洁、性能好 生态不如 Redux
Redux Toolkit 大型团队协作 规范、中间件丰富、DevTools 强大 样板代码多、学习成本高

我个人推荐 Zustand。为什么?因为它够轻、够快、够简单。你看它的写法:

import { create } from 'zustand';

const useEditorStore = create((set) => ({
  components: [],
  selectedId: null,
  history: [],
  addComponent: (component) =>
    set((state) => ({ components: [...state.components, component] })),
  selectComponent: (id) => set({ selectedId: id }),
  undo: () => set((state) => {
    const prev = state.history.pop();
    return prev ? { components: prev } : state;
  }),
}));

你看,一个 store 就搞定了组件树、选中状态和历史记录。而且 Zustand 默认做了浅比较,不会像 Context 那样一改全刷。

注意:如果你团队有严格的 TypeScript 规范,或者需要时间旅行调试,那 Redux Toolkit 更合适。但说实话,低代码编辑器这种场景,Zustand 的灵活性和性能优势更明显。

我曾经在一个项目里用 Redux Toolkit 做状态管理,结果光定义 slice 和 action 就写了 200 多行。后来换成 Zustand,代码量直接砍半,而且拖拽时的性能提升肉眼可见。

2.4 我的最终选型建议

好了,总结一下我的推荐组合:

  • 前端框架:React 18+(Hooks 模式)
  • 拖拽库:dnd-kit(最新版)
  • 状态管理:Zustand(配合 immer 处理不可变数据)

这个组合我用了两年,做了三个编辑器项目,没出过大问题。当然,如果你团队 Vue 更强,也可以换成 Vue 3 + vue-draggable-next + Pinia,思路是一样的。

下一章,我们会开始搭建编辑器的整体架构。到时候你会看到,选对了工具,架构设计会变得特别顺畅。