dnd-kit logo

dnd-kit

现代化的 React 拖拽库,以无障碍为核心,提供模块化、高性能的拖拽体验。

精选工具拖拽排序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 等新包。

基本信息

快速上手

安装

# 安装核心包
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: &#123; distance, delay &#125;
KeyboardSensor键盘操作(Space、Enter、方向键)sortableKeyboardCoordinates
TouchSensor触屏设备,长按激活activationConstraint: &#123; delay: 250 &#125;

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/reactReact 适配器
@dnd-kit/vueVue 适配器
@dnd-kit/svelteSvelte 适配器

核心特性

  • 无障碍优先(A11y First):内建 ARIA live regions、键盘导航(Space 拾取/放下、方向键移动、Escape 取消)、屏幕阅读器支持,符合 WCAG 2.1 AA 标准
  • 模块化设计:核心库仅 ~6KB(gzipped),按需引入排序、修饰器等功能包
  • 多输入方式:统一支持鼠标、触摸、键盘三种输入,通过传感器系统灵活组合
  • 碰撞检测可插拔:4 种内置算法 + 自定义算法支持,适应列表、网格、树等不同布局
  • 修饰器系统:动态约束拖拽范围(轴限制、边界限制、父元素限制、网格吸附)
  • 自动滚动:拖拽接近容器边缘时自动滚动,支持多级嵌套滚动容器
  • 虚拟化支持verticalListSortingStrategyhorizontalListSortingStrategy 原生支持虚拟化列表,配合 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 加速transformopacity 动画走 GPU 合成层
  • DragOverlay 模式:拖拽时渲染覆盖层,避免频繁操作原始 DOM
  • React.memo 友好:SortableItem 可通过 React.memo 优化,避免拖拽过程中不必要的重渲染

性能优化建议

  1. 使用 React.memo 包裹 SortableItem:防止父组件 state 变化导致所有项重渲染
  2. 稳定 items 数组引用:避免每次渲染创建新数组导致 SortableContext 重置
  3. 合理选择碰撞检测算法closestCenter 计算量小于 rectIntersection
  4. 大数据量启用虚拟化:配合 react-window / react-virtuoso,只渲染可见区域
  5. 避免在 onDragOver 中做昂贵操作:此事件在拖拽过程中频繁触发

安全注意事项

  • dnd-kit 不涉及用户数据输入,无 XSS 风险
  • 拖拽数据(data 属性)避免包含敏感信息
  • 服务端渲染(SSR)安全,但需注意 hydration 时 DOM 一致性

已知性能瓶颈

  • 超过 1000 个元素的碰撞检测可能出现可测量延迟
  • CSS scale / transform 父容器:可能导致碰撞检测坐标计算不准确
  • 大量 onDragOver 回调:频繁触发可能导致 state 更新风暴

技术对比

与 React 拖拽库对比

维度dnd-kitreact-beautiful-dndReact-DnDPragmatic 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 新架构(实验性)

最佳实践

生产环境最佳实践

  1. 始终设置 touch-action: none:在可拖拽元素上阻止移动端误触滚动

    .draggable { touch-action: none; }
  2. 使用 DragOverlay 渲染覆盖层:避免拖拽时影响原始 DOM

    <DragOverlay>
      {activeId ? <SortableItem id={activeId} /> : null}
    </DragOverlay>
  3. 使用 React.memo 优化 SortableItem

    const SortableItem = React.memo(({ id }) => {
      const { ... } = useSortable({ id })
      return <li ref={setNodeRef} style={style} {...listeners}>{id}</li>
    })
  4. 合理选择碰撞检测算法

    • 列表排序 → closestCenter
    • 网格布局 → closestCorners
    • 跨容器拖拽 → pointerWithin
  5. 使用 Drag Handle 模式:在列表中使用拖拽手柄,手柄设 touch-action: none,其余区域保持可滚动

  6. 稳定 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,新架构尚处实验阶段

学习资源

2026 年现状

版本双轨制

dnd-kit 目前处于架构重写阶段,存在两套并行版本:

架构状态
Legacy@dnd-kit/[email protected]稳定维护,仅 bug 修复
新版@dnd-kit/react(基于 @dnd-kit/dom2026 年 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。