做前端开发的兄弟们,每天最怕听到的一句话是什么?
不是“需求变了”,也不是“后端接口500”,而是产品经理兴致勃勃地跑过来说:“这个列表,我要能拖拽排序,还要丝滑,要像Trello那样,对了,手机上也要能用!”
此时你的内心大概率是崩溃的。原生的 HTML5 Drag and Drop API 简直就是一场灾难,不仅 API 设计得像上个世纪的产物,各家浏览器的兼容性更是让你想砸键盘。
于是,你打开了 GitHub,寻找那个传说中的救世主——react-beautiful-dnd(简称 rbd)。
但今天这篇文章,不是来给你安利它的,而是来祭奠它的。
没错,这个拥有 30k+ Star,曾经是 React 生态中拖拽交互“颜值天花板”的项目,已经被 Atlassian 官方归档(Archived)了。
但这并不妨碍我们最后一次“验尸”,看看它到底凭什么曾经统治了我们的看板。
核心亮点:为什么它曾是唯一的真神?
在 react-beautiful-dnd 出现之前,React 社区的拖拽库大多是“能用就行”。但 RBD 的出现,直接把标准拉到了“艺术品”的层级。
1. 物理引擎般的“自然交互”
很多拖拽库的实现非常生硬:你把一个元素拖起来,下面的元素瞬间闪现填补空位,视觉上非常突兀。
RBD 最强的地方在于它的动画设计。当你拖动一个 Item 时,列表中的其他元素会像有磁力一样,平滑地、带有物理惯性地让出位置。这种“让路”的动画(Animate out of the way),让用户感觉到他操作的不是冷冰冰的 DOM,而是有实体的卡片。
更绝的是,它不需要你写一行 CSS Transition,开箱即送。

2. 真正的无障碍(Accessibility)之王
现在的开源项目,不做 a11y(无障碍)都不好意思出来混。但 RBD 是真的做到了极致。
大多数拖拽库对键盘支持是 0。RBD 允许用户使用键盘聚焦某个卡片,按空格键“提起”,再用方向键移动,最后按空格“放下”。它甚至内置了针对屏幕阅读器的英文提示音。对于那些无法使用鼠标的用户,RBD 依然提供了完整的拖拽体验。这点上,很多竞品至今难以望其项背。
3. 虚拟列表支持:千万级数据也不卡
搞拖拽最怕什么?卡顿。如果你有 10,000 个待办事项,普通的拖拽库直接能把浏览器搞崩。
RBD 在 README 里极其嚣张地写着:Support for virtual lists – unlocking 10,000 items @ 60fps。它通过精妙的计算和对可视区域的判断,完美兼容了虚拟滚动技术。这在当年,简直就是性能优化的黑魔法。
竞品对比:后 RBD 时代的群雄逐鹿
既然 react-beautiful-dnd 已经官方宣布由于维护成本过高而停止维护,那我们现在该用什么?来看看现在的战局。
1. 官方指定继承人:Pragmatic Drag and Drop
Atlassian 在废弃 RBD 的同时,推出了继任者 Pragmatic Drag and Drop。
* 对比 RBD:RBD 是高度封装的,给你什么你就得用什么(Opnionated);而 Pragmatic 更底层、更灵活,不仅支持 List,还支持文件上传、Grid 等任意布局。
* 槽点:RBD 的 API 是真的简单(声明式),Pragmatic 的 API 更加底层,上手难度直线上升。如果你只是想做一个简单的看板,用这个有点“大炮打蚊子”。
2. 民间复活版:@hello-pangea/dnd
这是目前社区最推荐的无痛替代方案。
* 背景:由于 RBD 长期不修 Bug(尤其是 React 18 的 Strict Mode 兼容性问题),社区大佬忍不住了,直接 Fork 了一个版本叫 hello-pangea/dnd。
* 优势:它就是 RBD 的克隆体。你只需要把 import { DragDropContext } ... from 'react-beautiful-dnd' 改成 from '@hello-pangea/dnd',代码一行都不用动,Bug 全修好了,React 18 也支持了。
* 评价:如果你还在维护用 RBD 写的旧项目,请立刻无脑切到这个库。
3. 老牌劲敌:React DnD
- 对比:
react-dnd一直是 RBD 的死对头。RBD 专注于“列表”,而react-dnd是一套通用的拖拽原语。 - 劣势:配置极其繁琐,写一个简单的排序需要写大量的样板代码。如果你不需要搞什么骚操作,只是简单的列表排序,用
react-dnd简直是自虐。
部署与使用:最后的挽歌
虽然它归档了,但它的 API 设计之优雅,依然值得我们学习。它的核心概念只有三个,简直是声明式编程的教科书。
先看看这极简的架构图:

核心三剑客
- DragDropContext: 包裹整个拖拽区域,处理
onDragEnd事件。 - Droppable: 定义哪些区域可以“接收”被拖拽的元素(比如“待办”列、“完成”列)。
- Draggable: 定义哪些元素可以被“抓起来”。
极速上手代码
import React from 'react';
// ⚠️ 警告:新项目建议使用 @hello-pangea/dnd 替代下方引用
import { DragDropContext, Droppable, Draggable } from 'react-beautiful-dnd';
const list = [{id: '1', content: '点个赞'}, {id: '2', content: '关注我'}];
function App() {
const onDragEnd = (result) => {
// 这里处理数组排序逻辑,比如使用 result.source.index 和 result.destination.index
console.log('拖拽结束,快去更新你的 State');
};
return (
<DragDropContext onDragEnd={onDragEnd}>
<Droppable droppableId="droppable">
{(provided) => (
<div
{...provided.droppableProps}
ref={provided.innerRef}
>
{list.map((item, index) => (
<Draggable key={item.id} draggableId={item.id} index={index}>
{(provided) => (
<div
ref={provided.innerRef}
{...provided.draggableProps}
{...provided.dragHandleProps}
style={{
padding: 16,
margin: '0 0 8px 0',
background: '#fff',
...provided.draggableProps.style
}}
>
{item.content}
</div>
)}
</Draggable>
))}
{provided.placeholder} {/* 占位符,防止高度塌陷 */}
</div>
)}
</Droppable>
</DragDropContext>
);
}
这段代码最迷人的地方在于 Function as Child (Render Props) 模式的使用。虽然现在 Hooks 流行了,但在当时,这种将 DOM ref 和 props 通过函数参数透传出来的设计,极大地保证了灵活性——它不增加额外的 DOM 节点,完全尊重你的布局。
结语
react-beautiful-dnd 的归档,标志着一个时代的结束。它教会了我们:拖拽不仅仅是功能的实现,更是物理手感和视觉反馈的艺术。
如果你是维护旧项目,请出门左转找 @hello-pangea/dnd;如果你是新项目,可以挑战一下 Atlassian 的新宠 Pragmatic drag and drop。
至于 RBD,就让它留在我们的 package.json 历史记录里,成为一座丰碑吧。
项目地址: https://github.com/atlassian/react-beautiful-dnd
