dnd-kit
现代化的 React 拖拽库,以无障碍为核心,提供模块化、高性能的拖拽体验。
现代化 React 拖拽库,以无障碍为核心,基于自定义指针事件系统,模块化设计提供高性能拖拽体验,正在向框架无关架构演进。
dnd-kit
现代化的 React 拖拽库,以无障碍为核心,提供模块化、高性能的拖拽体验。
技术简介说明
dnd-kit 是一款专为 React 设计的现代化拖拽工具集,由 Claudéric Demers 创建。它以**无障碍(Accessibility)**为第一设计原则,内建完整的 ARIA 支持、键盘导航和屏幕阅读器友好特性。
与传统拖拽库不同,dnd-kit 不依赖 HTML5 Drag and Drop API,而是基于自定义指针事件系统实现。这使得它在触摸设备、键盘操作和跨浏览器一致性方面表现出色,但也意味着不支持从桌面拖入文件到浏览器。
dnd-kit 采用高度模块化的设计理念,核心库仅提供基础能力(DndContext、碰撞检测、传感器系统),排序、修饰器等功能通过独立包提供。2026 年,dnd-kit 正在进行架构重写,从 React-only 转向框架无关的模块化架构,已发布 @dnd-kit/react、@dnd-kit/vue、@dnd-kit/svelte 等新包。
基本信息
- 官网: https://dndkit.com
- GitHub: https://github.com/clauderic/dnd-kit
- License: MIT
- 最新版本: Legacy
@dnd-kit/[email protected](2024.12);新版@dnd-kit/react(2026.06) - 主要维护者: Claudéric Demers(@clauderic)
- npm 周下载量: ~280 万(@dnd-kit/core)
- GitHub Stars: ~17,300+
快速上手
安装
# 安装核心包
npm install @dnd-kit/core @dnd-kit/utilities
# 安装排序预设(如需列表排序)
npm install @dnd-kit/sortable
# 安装修饰器(如需约束拖拽范围)
npm install @dnd-kit/modifiers
# 或使用 pnpm
pnpm add @dnd-kit/core @dnd-kit/sortable @dnd-kit/utilities @dnd-kit/modifiers新架构安装(2026 年实验性):
npm install @dnd-kit/react @dnd-kit/dom
基础配置
dnd-kit 无需全局 Provider 配置(传统架构),只需在组件中引入 DndContext:
import { DndContext, PointerSensor, useSensor, useSensors } from '@dnd-kit/core'
function App() {
const sensors = useSensors(
useSensor(PointerSensor, {
activationConstraint: { distance: 8 }, // 移动 8px 后激活拖拽
})
)
return (
<DndContext sensors={sensors} onDragEnd={handleDragEnd}>
{/* 拖拽内容 */}
</DndContext>
)
}最小示例
import { useState } from 'react'
import { DndContext, closestCenter } from '@dnd-kit/core'
import {
SortableContext,
useSortable,
verticalListSortingStrategy,
} from '@dnd-kit/sortable'
import { CSS } from '@dnd-kit/utilities'
// 可排序项组件
function SortableItem({ id }) {
const { attributes, listeners, setNodeRef, transform, transition } =
useSortable({ id })
const style = {
transform: CSS.Transform.toString(transform),
transition,
}
return (
<li ref={setNodeRef} style={style} {...attributes} {...listeners}>
{id}
</li>
)
}
// 主组件
export function SortableList() {
const [items, setItems] = useState(['Item 1', 'Item 2', 'Item 3'])
function handleDragEnd(event) {
const { active, over } = event
if (active.id !== over.id) {
setItems((items) => {
const oldIndex = items.indexOf(active.id)
const newIndex = items.indexOf(over.id)
// 数组重排
const newItems = [...items]
const [removed] = newItems.splice(oldIndex, 1)
newItems.splice(newIndex, 0, removed)
return newItems
})
}
}
return (
<DndContext collisionDetection={closestCenter} onDragEnd={handleDragEnd}>
<SortableContext items={items} strategy={verticalListSortingStrategy}>
<ul>
{items.map((id) => (
<SortableItem key={id} id={id} />
))}
</ul>
</SortableContext>
</DndContext>
)
}核心概念与架构
架构设计
dnd-kit 的架构围绕以下核心层构建:
┌─────────────────────────────────┐
│ DndContext │ ← 顶层 Provider,管理拖拽状态
├─────────────────────────────────┤
│ Sensors(传感器) │ ← 检测输入:Pointer / Keyboard / Touch
├─────────────────────────────────┤
│ Collision Detection(碰撞检测) │ ← 判断拖拽元素与目标的关系
├─────────────────────────────────┤
│ Modifiers(修饰器) │ ← 动态修改坐标(约束、吸附等)
├─────────────────────────────────┤
│ Draggable / Droppable │ ← 使元素可拖拽 / 可放置
├─────────────────────────────────┤
│ Sortable(排序预设) │ ← 列表排序的高级抽象
└─────────────────────────────────┘
传感器系统(Sensors)
传感器是 dnd-kit 的输入检测层,决定拖拽如何被触发:
| 传感器 | 说明 | 关键配置 |
|---|---|---|
| PointerSensor | 鼠标 / 触控笔输入 | activationConstraint: { distance, delay } |
| KeyboardSensor | 键盘操作(Space、Enter、方向键) | sortableKeyboardCoordinates |
| TouchSensor | 触屏设备,长按激活 | activationConstraint: { delay: 250 } |
activationConstraint 设计:控制传感器激活条件,例如 distance: 8 表示指针移动 8px 后才激活拖拽(防止点击误触),delay: 250 表示长按 250ms 后激活。
碰撞检测算法
| 算法 | 原理 | 适用场景 |
|---|---|---|
| rectIntersection | 检测拖拽元素矩形与目标矩形的交叉 | 通用场景(默认) |
| closestCenter | 基于元素中心点距离 | 排序列表(推荐) |
| closestCorners | 基于元素四角距离 | 网格布局 |
| pointerWithin | 检测指针位于哪个目标内 | 多容器跨容器拖拽 |
修饰器(Modifiers)
修饰器动态修改传感器检测到的坐标,用于约束拖拽行为:
import { restrictToVerticalAxis } from '@dnd-kit/modifiers'
<DndContext modifiers={[restrictToVerticalAxis]}>
{/* 只能垂直拖拽 */}
</DndContext>新架构(2026 年)
dnd-kit 正在从 React-only 转向框架无关的模块化架构:
| 新包 | 职责 |
|---|---|
@dnd-kit/abstract | 框架无关的抽象核心 |
@dnd-kit/dom | 框架无关的 DOM 操作层 |
@dnd-kit/collision | 碰撞检测模块 |
@dnd-kit/state | 响应式状态管理 |
@dnd-kit/react | React 适配器 |
@dnd-kit/vue | Vue 适配器 |
@dnd-kit/svelte | Svelte 适配器 |
核心特性
- 无障碍优先(A11y First):内建 ARIA live regions、键盘导航(Space 拾取/放下、方向键移动、Escape 取消)、屏幕阅读器支持,符合 WCAG 2.1 AA 标准
- 模块化设计:核心库仅 ~6KB(gzipped),按需引入排序、修饰器等功能包
- 多输入方式:统一支持鼠标、触摸、键盘三种输入,通过传感器系统灵活组合
- 碰撞检测可插拔:4 种内置算法 + 自定义算法支持,适应列表、网格、树等不同布局
- 修饰器系统:动态约束拖拽范围(轴限制、边界限制、父元素限制、网格吸附)
- 自动滚动:拖拽接近容器边缘时自动滚动,支持多级嵌套滚动容器
- 虚拟化支持:
verticalListSortingStrategy和horizontalListSortingStrategy原生支持虚拟化列表,配合 react-window / react-virtuoso 使用 - DragOverlay:渲染拖拽覆盖层,避免操作原始 DOM,实现流畅的视觉效果
- TypeScript 原生:完整的类型定义,零配置类型支持
- 框架无关演进:新架构支持 React、Vue、Svelte、Solid
生态图
┌──────────────────┐
│ @dnd-kit/core │ ← 核心:DndContext、传感器、碰撞检测
└────────┬─────────┘
│
┌────────────────┼────────────────┐
│ │ │
┌────────▼──────┐ ┌──────▼───────┐ ┌──────▼────────┐
│ @dnd-kit/ │ │ @dnd-kit/ │ │ @dnd-kit/ │
│ sortable │ │ modifiers │ │ utilities │
│ (排序预设) │ │ (约束修饰器) │ │ (CSS 工具) │
└───────────────┘ └──────────────┘ └───────────────┘
2026 年新架构(框架无关):
┌──────────────────────────────────────────────┐
│ @dnd-kit/abstract → @dnd-kit/dom → 适配器层 │
│ │
│ react │ vue │ svelte │ solid │
└──────────────────────────────────────────────┘
社区生态:
┌──────────────────────────────────────────────┐
│ • @dnd-kit/accessibility (无障碍工具) │
│ • 配合 react-window / react-virtuoso (虚拟化) │
│ • 社区碰撞检测算法扩展 │
│ • Puck Editor (基于 dnd-kit 的可视化编辑器) │
└──────────────────────────────────────────────┘
关键依赖
- 无外部运行时依赖:
@dnd-kit/core不依赖任何第三方库 - React ≥ 16.8(需要 Hooks 支持)
- 新架构:依赖
@dnd-kit/dom、@dnd-kit/abstract、@dnd-kit/state等内部包
适用场景
- Kanban 看板:多容器拖拽、跨列移动卡片,配合
pointerWithin碰撞检测和onDragOver实时更新 - 排序列表:Todo 列表、任务优先级排序、播放列表排序,使用
@dnd-kit/sortable预设 - 网格布局拖拽:图标排列、仪表盘小部件布局,使用
closestCorners碰撞检测 - 跨容器拖拽:从"待办"列表拖到"已完成"列表,需要状态同步
- 树形结构拖拽:文件管理器、组织架构调整(需自行实现碰撞检测和状态管理)
- 表单构建器:拖拽组件到画布,配合修饰器约束放置区域
- 虚拟化大列表:配合 react-virtuoso 处理数千条目的排序场景
开发与工程化
开发流程
# 克隆仓库
git clone https://github.com/clauderic/dnd-kit.git
cd dnd-kit
# 安装依赖
yarn install
# 启动开发服务器(storybook)
yarn dev
# 构建
yarn build
# 测试
yarn test项目结构
dnd-kit/
├── packages/
│ ├── core/ # @dnd-kit/core
│ ├── sortable/ # @dnd-kit/sortable
│ ├── modifiers/ # @dnd-kit/modifiers
│ ├── utilities/ # @dnd-kit/utilities
│ └── accessibility/ # @dnd-kit/accessibility
├── stories/ # Storybook 示例和演示
└── test/ # 测试文件
CI/CD 集成
- 使用 GitHub Actions 进行持续集成
- 每个 PR 自动运行类型检查、lint、测试
- 发布通过 changesets 管理版本
版本管理
- 使用 changesets 进行版本管理
- 每次 PR 需要附带 changeset 文件说明变更类型
性能与安全
性能特点
- CSS transform 移动:使用
transform而非top/left/margin,避免触发重排(reflow),保持 60fps - GPU 加速:
transform和opacity动画走 GPU 合成层 - DragOverlay 模式:拖拽时渲染覆盖层,避免频繁操作原始 DOM
- React.memo 友好:SortableItem 可通过
React.memo优化,避免拖拽过程中不必要的重渲染
性能优化建议
- 使用
React.memo包裹 SortableItem:防止父组件 state 变化导致所有项重渲染 - 稳定
items数组引用:避免每次渲染创建新数组导致 SortableContext 重置 - 合理选择碰撞检测算法:
closestCenter计算量小于rectIntersection - 大数据量启用虚拟化:配合 react-window / react-virtuoso,只渲染可见区域
- 避免在
onDragOver中做昂贵操作:此事件在拖拽过程中频繁触发
安全注意事项
- dnd-kit 不涉及用户数据输入,无 XSS 风险
- 拖拽数据(
data属性)避免包含敏感信息 - 服务端渲染(SSR)安全,但需注意 hydration 时 DOM 一致性
已知性能瓶颈
- 超过 1000 个元素的碰撞检测可能出现可测量延迟
- CSS
scale/transform父容器:可能导致碰撞检测坐标计算不准确 - 大量
onDragOver回调:频繁触发可能导致 state 更新风暴
技术对比
与 React 拖拽库对比
| 维度 | dnd-kit | react-beautiful-dnd | React-DnD | Pragmatic DnD |
|---|---|---|---|---|
| 维护状态 | ✅ 活跃 | ❌ 已弃用(2022) | ⚠️ 4 年无更新 | ✅ Atlassian 支持 |
| 包体积 | ~6KB | ~30KB | ~23KB | ~3.5KB |
| 无障碍 | ✅ 内建 WCAG 2.1 AA | ✅ 键盘支持 | ⚠️ 需手动实现 | ⚠️ 需手动组合 |
| 触摸支持 | ✅ 原生 | ⚠️ 有限 | ⚠️ 需 TouchBackend | ⚠️ 需配置 |
| HTML5 DnD API | ❌ 不使用 | ❌ 不使用 | ✅ 使用 | ✅ 使用 |
| 外部文件拖入 | ❌ 不支持 | ❌ 不支持 | ✅ 支持 | ✅ 支持 |
| 虚拟化 | ✅ 原生支持 | ⚠️ 较差 | ⚠️ 需自行实现 | ✅ 原生支持 |
| 布局类型 | 列表 / 网格 / 树 / 通用 | 仅列表 | 通用 | 通用 |
| 学习曲线 | 中等 | 简单 | 陡峭 | 中等 |
| 框架支持 | React / Vue / Svelte / Solid | 仅 React | 仅 React | 框架无关 |
| 社区趋势 | ⬆️ 增长(默认选择) | ⬇️ 遗留 | ➡️ 停滞 | ⬆️ 增长 |
选型建议
| 场景 | 推荐 |
|---|---|
| React 项目通用拖拽 | dnd-kit(默认选择) |
| 需要从桌面拖入文件 | Pragmatic DnD 或 React-DnD |
| 遗留 react-beautiful-dnd 项目 | 迁移到 dnd-kit 或 @hello-pangea/dnd |
| 极致小包体积 | Pragmatic DnD |
| Vue / Svelte 项目 | dnd-kit 新架构(实验性) |
最佳实践
生产环境最佳实践
-
始终设置
touch-action: none:在可拖拽元素上阻止移动端误触滚动.draggable { touch-action: none; } -
使用 DragOverlay 渲染覆盖层:避免拖拽时影响原始 DOM
<DragOverlay> {activeId ? <SortableItem id={activeId} /> : null} </DragOverlay> -
使用
React.memo优化 SortableItem:const SortableItem = React.memo(({ id }) => { const { ... } = useSortable({ id }) return <li ref={setNodeRef} style={style} {...listeners}>{id}</li> }) -
合理选择碰撞检测算法:
- 列表排序 →
closestCenter - 网格布局 →
closestCorners - 跨容器拖拽 →
pointerWithin
- 列表排序 →
-
使用 Drag Handle 模式:在列表中使用拖拽手柄,手柄设
touch-action: none,其余区域保持可滚动 -
稳定
items引用:使用useMemo或确保 state 更新时创建新数组而非修改原数组
常见陷阱
| 陷阱 | 说明 | 解决方案 |
|---|---|---|
| 过度重渲染 | SortableItem 在拖拽中频繁 re-render | 使用 React.memo |
| ID 冲突 | draggable ID 必须唯一 | 确保 ID 不与同 context 内其他 ID 重复 |
| 跨容器状态不一致 | 只监听 onDragEnd 不更新中间状态 | 配合 onDragOver 实时更新 |
| 忘记 peerDependencies | @dnd-kit/sortable 需要 @dnd-kit/core | 确保安装所有必需包 |
| CSS scale 影响碰撞检测 | 父容器有 transform: scale() | 避免在可拖拽容器上使用 CSS scale |
| 虚拟化列表排序策略错误 | 使用默认的 rectSortingStrategy | 虚拟化列表必须用 verticalListSortingStrategy |
技术局限与边界
已知限制
| 限制 | 说明 |
|---|---|
| 不支持外部文件拖入 | 不基于 HTML5 DnD API,无法从桌面拖入文件到浏览器 |
| 不支持跨窗口拖拽 | 同上原因 |
| 学习曲线较陡 | 模块化设计需要理解 DndContext、传感器、碰撞检测、修饰器等多个概念 |
| 样板代码较多 | 相比 react-beautiful-dnd 的开箱即用,需要更多配置代码 |
| 动画需手动配置 | 不内置动画过渡,需通过 transition 属性自行配置 |
| CSS transform 冲突 | 父容器使用 CSS scale/transform 时碰撞检测可能不准确 |
| Legacy → 新架构迁移 | 从 @dnd-kit/core 迁移到 @dnd-kit/react 需要重大重构 |
| 树形拖拽需自行实现 | 无内置树形结构支持,需自定义碰撞检测和状态管理 |
不适用场景
- 文件上传拖拽区域(Dropzone)→ 推荐 react-dropzone
- 跨浏览器窗口拖拽 → 需要 HTML5 DnD API,推荐 React-DnD
- 非 React 项目(Legacy)→ 旧架构仅支持 React,新架构尚处实验阶段
学习资源
- 官方文档:https://dndkit.com
- Legacy 文档:https://dndkit.com/legacy/introduction/installation
- 新架构文档:https://dndkit.com/react/quickstart
- 官方示例:https://examples.dndkit.com
- GitHub 仓库:https://github.com/clauderic/dnd-kit
- GitHub Discussions:https://github.com/clauderic/dnd-kit/discussions
- Slack 社区:https://dnd-kit.slack.com
- 教程:A Beginner's Guide to DnD Kit (Dev.to)
- 视频教程:React Drag & Drop Made Easy (YouTube)
- 中文翻译:掘金 - dnd-kit 中文文档翻译
2026 年现状
版本双轨制
dnd-kit 目前处于架构重写阶段,存在两套并行版本:
| 架构 | 包 | 状态 |
|---|---|---|
| Legacy | @dnd-kit/[email protected] | 稳定维护,仅 bug 修复 |
| 新版 | @dnd-kit/react(基于 @dnd-kit/dom) | 2026 年 6 月发布,活跃开发中 |
发展趋势
- 活跃维护:被 PkgPulse 评为"2026 年 React DnD 的默认选择"
- 框架无关化:新架构从 React-only 扩展至 Vue、Svelte、Solid
- 社区持续增长:17,300+ Stars,~280 万周下载量
- react-beautiful-dnd 弃用后的首选替代:被 Reddit、Dev.to 等社区广泛推荐
社区活跃度
- 持续有新的 Issue、Discussion 和 PR 提交
- 核心维护者 Claudéric Demers 持续推进新架构重写
- 生态项目如 Puck Editor(基于 dnd-kit 的可视化编辑器)持续增长
未来路线图
- 完善
@dnd-kit/react的 API 稳定性和文档 - 推进 Vue、Svelte、Solid 适配器的正式版本
- 引入
DragDropManager概念替代DndContext - 改进插件系统、传感器、修饰器的扩展机制
- 目标:更易学习、更灵活、性能更优
选型结论
2026 年,dnd-kit 是 React 拖拽的默认选择。 对于新项目,除非需要从桌面拖入文件或跨窗口拖拽(此时选择 Pragmatic DnD 或 React-DnD),否则应优先考虑 dnd-kit。