Vue-Draggable-Plus logo

Vue-Draggable-Plus

Vue 3 / Vue 2.7 拖拽排序库,基于 SortableJS 封装,提供组件、组合式函数和指令三种使用方式,是 Vue 生态拖拽排序的首选方案。

拖拽排序Vue拖拽库开源

Vue 3/2.7 拖拽排序库,基于 SortableJS 封装,提供组件、组合式函数和指令三种使用方式,与 Vue 响应式深度集成,实现拖拽操作与数据双向同步。

Vue-Draggable-Plus

Vue 3 / Vue 2.7 拖拽排序库,基于 SortableJS 封装,提供组件、组合式函数和指令三种使用方式,是 Vue 生态拖拽排序的首选方案。

技术简介说明

Vue-Draggable-Plus 是一款专为 Vue 3 和 Vue 2.7+ 设计的拖拽排序库,由 Alfred-Skyblue 创建。它基于成熟的 SortableJS 封装,在底层 SortableJS 的所有拖拽能力之上,添加了 Vue 响应式集成层,实现了拖拽操作与 Vue 数据的双向同步。

Vue-Draggable-Plus 的核心设计理念是三种使用方式自由切换:组件模式(<VueDraggable>)、组合式函数模式(useDraggable())和指令模式(v-draggable)。这使其能灵活适配不同场景——简单列表用组件模式,复杂逻辑用组合式函数,最小改动用指令模式。特别是 target 选项的引入,解决了与第三方 UI 组件库(如 Element Plus 表格、Naive UI 表格)集成的长期痛点。

Vue-Draggable-Plus 已获得 尤雨溪(Evan You)的公开推荐,成为 Vue 生态拖拽排序的事实标准。npm 周下载量约 25 万,GitHub 4,000+ Stars,正在快速替代已停止维护的旧版 vuedraggable 和 vue.draggable.next。

基本信息

快速上手

安装

# npm
npm install vue-draggable-plus
 
# 或 pnpm
pnpm add vue-draggable-plus
 
# 或 yarn
yarn add vue-draggable-plus
 
# CDN
<script src="https://cdn.jsdelivr.net/npm/vue-draggable-plus/dist/index.umd.js"></script>

基础配置

Vue-Draggable-Plus 无需全局注册,直接在组件中引入使用:

<script setup>
import { VueDraggable } from 'vue-draggable-plus'
import { ref } from 'vue'
 
const list = ref([
  { name: 'Item 1', id: 1 },
  { name: 'Item 2', id: 2 },
  { name: 'Item 3', id: 3 },
])
</script>
 
<template>
  <VueDraggable v-model="list" :animation="150">
    <div v-for="item in list" :key="item.id">
      {{ item.name }}
    </div>
  </VueDraggable>
</template>

最小示例

方式一:组件模式(最简单)

<script setup>
import { VueDraggable } from 'vue-draggable-plus'
import { ref } from 'vue'
 
const list = ref(['Apple', 'Banana', 'Cherry', 'Date'])
</script>
 
<template>
  <VueDraggable v-model="list" :animation="150" ghost-class="ghost">
    <div v-for="item in list" :key="item" class="item">
      {{ item }}
    </div>
  </VueDraggable>
</template>
 
<style>
.item {
  padding: 10px;
  margin: 5px;
  background: #f0f0f0;
  border-radius: 4px;
  cursor: grab;
}
.ghost {
  opacity: 0.5;
  background: #c8ebfb;
}
</style>

方式二:组合式函数模式(灵活)

<script setup>
import { useDraggable } from 'vue-draggable-plus'
import { ref } from 'vue'
 
const el = ref(null)
const list = ref([
  { name: 'Task 1', id: 1 },
  { name: 'Task 2', id: 2 },
  { name: 'Task 3', id: 3 },
])
 
useDraggable(el, list, {
  animation: 150,
  onUpdate: (event) => {
    console.log('排序更新:', event.oldIndex, '->', event.newIndex)
  }
})
</script>
 
<template>
  <div ref="el">
    <div v-for="item in list" :key="item.id" class="item">
      {{ item.name }}
    </div>
  </div>
</template>

方式三:指令模式(最小改动)

<script setup>
import { vDraggable } from 'vue-draggable-plus'
import { ref } from 'vue'
 
const list = ref(['Item A', 'Item B', 'Item C'])
</script>
 
<template>
  <div v-draggable="list">
    <div v-for="item in list" :key="item">
      {{ item }}
    </div>
  </div>
</template>

核心概念与架构

三层架构设计

Vue-Draggable-Plus 的核心架构分为三层:

┌─────────────────────────────────────┐
│       第三层:数据驱动同步层          │
│  Vue 响应式桥接(v-model 更新)      │
│  拖拽事件 → splice 修改数组 →        │
│  Vue 响应式 → DOM 更新              │
├─────────────────────────────────────┤
│       第二层:封装层                  │
│  ┌────────┐ ┌──────────┐ ┌───────┐ │
│  │ 组件   │ │组合式函数 │ │ 指令  │ │
│  │<Vue    │ │useDrag-  │ │v-     │ │
│  │Draggable│ │gable()   │ │draggable│
│  └───┬────┘ └────┬─────┘ └───┬───┘ │
│      │           │           │      │
├──────▼───────────▼───────────▼──────┤
│       第一层:SortableJS 基础层      │
│  原生 DOM 拖拽交互、动画、触摸支持   │
│  鼠标/触摸事件、ghost 元素、占位符   │
│  多列表组、克隆、Swap、MultiDrag    │
└─────────────────────────────────────┘

数据流

用户拖拽 DOM 元素
       ↓
SortableJS 触发事件(onEnd, onUpdate 等)
       ↓
vue-draggable-plus 拦截事件
       ↓
从 Sortable 事件计算 newOldIndex / newIndex
       ↓
通过 splice 修改 Vue 响应式数组(v-model 更新)
       ↓
Vue 响应式系统触发重新渲染
       ↓
DOM 反映新的数据顺序

核心设计哲学

SortableJS 在拖拽过程中直接操作 DOM(保证流畅视觉反馈),拖拽结束时反向还原 DOM 变更,转而通过 Vue 响应式数据驱动渲染。

这个设计确保了:

  • 拖拽过程中的视觉反馈流畅(直接 DOM 操作)
  • 数据一致性由 Vue 响应式系统保证
  • 拖拽结束后的 DOM 状态与数据状态一致

内部 VueDraggablePlus

  1. 通过 new Sortable(el, options) 在目标 DOM 元素上创建 Sortable 实例
  2. 绑定 Sortable 事件回调(onStartonEndonUpdateonAddonRemove 等)
  3. onEnd / onUpdate 处理器中读取 oldIndex / newIndex,通过 array.splice() 移动响应式数组中的元素
  4. 支持 v-model 双向绑定
  5. 组件卸载时自动销毁 Sortable 实例

与旧版 Vue.Draggable 的关键差异

差异旧版 vuedraggablevue-draggable-plus
DOM 结构要求&lt;draggable&gt; 必须是列表项的直接父元素通过 target 选择器定位任意容器
第三方组件集成困难(DOM 结构约束)优秀(target 指定目标容器)
使用方式仅组件组件 + 组合式函数 + 指令

核心特性

  • Vue 3 + Vue 2 双版本支持:兼容 Vue ≥ 3 和 Vue ≥ 2.7,覆盖绝大多数 Vue 项目
  • 三种使用方式:组件 &lt;VueDraggable&gt;、组合式函数 useDraggable()、指令 v-draggable
  • 完整继承 SortableJS 所有特性:动画、跨列表拖拽、克隆、交换、多选拖拽等
  • TypeScript 编写:完整的类型定义,零配置类型支持
  • v-model 双向数据绑定:拖拽排序自动同步数据
  • target 选项:指定任意 DOM 元素作为拖拽容器,解决第三方组件集成问题
  • customUpdate 属性(v0.6.0 新增):允许完全接管更新逻辑
  • 拖拽句柄handle 选项指定拖拽手柄
  • 过滤filter 选项排除不可拖拽的元素
  • 动画与过渡效果animation 参数 + ghostClass + &lt;TransitionGroup&gt; 集成
  • 跨列表拖拽group 配置实现跨列表元素转移
  • 触摸设备友好:继承 SortableJS 的触摸支持
  • 自动滚动:长列表拖拽时自动滚动

生态图

                    ┌──────────────────┐
                    │   SortableJS     │  ← 底层拖拽引擎
                    │   (核心库)       │
                    └────────┬─────────┘
                             │
                    ┌────────▼─────────┐
                    │ vue-draggable-   │  ← Vue 响应式封装
                    │ plus             │
                    │ (组件/函数/指令)  │
                    └────────┬─────────┘
                             │
            ┌────────────────┼────────────────┐
            │                │                │
   ┌────────▼──────┐ ┌──────▼───────┐ ┌──────▼────────┐
   │ 组件模式      │ │ 组合式函数   │ │ 指令模式      │
   │ <VueDraggable>│ │ useDraggable │ │ v-draggable   │
   │ (简单场景)    │ │ (复杂逻辑)   │ │ (最小改动)    │
   └───────────────┘ └──────────────┘ └───────────────┘

   Vue 拖拽生态对比:
   ┌──────────────────────────────────────────────┐
   │ 库名                  │ 状态      │ 推荐度   │
   │─────────────────────────────────────────────│
   │ vue-draggable-plus    │ ✅ 活跃   │ ⭐⭐⭐⭐⭐│
   │ vuedraggable (旧版)   │ ❌ 停止   │ ❌      │
   │ vue.draggable.next    │ ❌ 废弃   │ ❌      │
   │ VueUse useDraggable   │ ✅ 活跃   │ ⭐⭐⭐  │
   │ VueUse useSortable    │ ✅ 活跃   │ ⭐⭐⭐  │
   └──────────────────────────────────────────────┘

关键依赖

  • sortablejs:底层拖拽引擎(SortableJS),提供所有 DOM 拖拽能力
  • vue:Vue ≥ 3 或 Vue ≥ 2.7(响应式系统)

社区生态

  • VueUse 集成:VueUse 提供了 useSortable 集成,基于 SortableJS 的 composable
  • Element Plus 社区:广泛推荐使用 vue-draggable-plus 实现表格行拖拽
  • Naive UI 社区:同样推荐 vue-draggable-plus 作为拖拽方案

适用场景

  • 列表排序(Todo 列表、任务列表):经典排序场景,v-model 双向绑定 + 动画
  • 表格行拖拽排序(Element Plus / Naive UI 表格)target 选项指定表格 tbody,完美集成
  • 看板/卡片拖拽(Kanban Board):跨列表拖拽 + 克隆模式
  • 跨列表元素转移:从"待办"拖到"已完成",使用 group 配置
  • 嵌套列表拖拽:支持两级嵌套排序
  • 表单字段排序:拖拽调整表单字段的显示顺序
  • 仪表盘小部件排序:拖拽调整布局顺序

开发与工程化

开发流程

# 克隆仓库
git clone https://github.com/Alfred-Skyblue/vue-draggable-plus.git
cd vue-draggable-plus
 
# 安装依赖
pnpm install
 
# 启动开发服务器
pnpm dev
 
# 构建
pnpm build
 
# 测试
pnpm test

项目结构

vue-draggable-plus/
├── src/
│   ├── component.ts       # <VueDraggable> 组件
│   ├── composables.ts     # useDraggable() 组合式函数
│   ├── directive.ts       # v-draggable 指令
│   ├── VueDraggablePlus.ts # 核心类(封装 SortableJS)
│   └── types.ts           # TypeScript 类型定义
├── demo/                   # 演示项目
├── docs/                   # 文档
└── tests/                  # 测试

构建输出

  • ESM 格式(dist/index.esm.js
  • UMD 格式(dist/index.umd.js
  • 类型定义(dist/index.d.ts

性能与安全

性能特点

  • 拖拽过程中不触发 Vue 响应式更新:SortableJS 直接操作 DOM,保证流畅度
  • 拖拽结束时才同步数据:避免拖拽过程中的频繁重渲染
  • 数百到数千个对象排序表现优秀:Reddit 社区用户报告

性能优化建议

  1. 使用唯一稳定的 key

    <!-- ✅ 正确:使用唯一 id -->
    <div v-for="item in list" :key="item.id">{{ item.name }}</div>
    <!-- ❌ 错误:使用 index -->
    <div v-for="(item, index) in list" :key="index">{{ item.name }}</div>
  2. 使用 ref() 而非 reactive() 包裹列表数据

    // ✅ 推荐
    const list = ref([{ id: 1, name: 'Item 1' }])
    // ❌ 避免:reactive() 可能导致拖拽操作静默失败
    const list = reactive([{ id: 1, name: 'Item 1' }])
  3. 设置 animation: 150:获得平滑动画效果

  4. 长列表启用自动滚动

    <VueDraggable v-model="list" :scroll="true">

安全注意事项

  • vue-draggable-plus 不涉及用户输入,无 XSS 风险
  • 拖拽数据避免包含敏感信息
  • 注意 SSR 水合匹配问题(Nuxt 项目需确保拖拽列表仅在客户端渲染)

已知性能瓶颈

  • 超大列表(>10,000 项):继承 SortableJS 的性能天花板
  • 虚拟滚动未优化:未针对虚拟滚动做专门优化
  • &lt;TransitionGroup&gt; 开销:组合使用时可能有额外 DOM 操作开销

技术对比

与 Vue 拖拽库对比

维度vue-draggable-plusvuedraggable (旧版)vue.draggable.nextVueUse useDraggable
Vue 3 支持✅ 完整❌ 不支持✅ 支持✅ 完整
Vue 2 支持✅ Vue 2.7+✅ Vue 2❌ 仅 Vue 3❌ 仅 Vue 3
维护状态✅ 活跃❌ 停止(6 年)❌ 停止(2024)✅ 活跃
使用方式组件 + 函数 + 指令仅组件仅组件仅 composable
TypeScript✅ 完整❌ 无❌ 无✅ 完整
target 选择器
customUpdate
第三方组件集成✅ 优秀❌ 困难❌ 困难⚠️ 单元素拖拽
列表排序✅ 完整✅ 完整✅ 完整❌ 单元素(非排序)
Stars~4,005~20,605~558-
尤雨溪推荐-

选型建议

场景推荐
Vue 3 项目拖拽排序vue-draggable-plus(首选)
Vue 2.7+ 项目vue-draggable-plus(首选)
旧版 vuedraggable 迁移vue-draggable-plus
Element Plus 表格行拖拽vue-draggable-plustarget 选项)
单元素自由拖拽(非排序)VueUse useDraggable
Vue 2.6 及以下旧版 vuedraggable(遗留项目)

最佳实践

生产环境最佳实践

  1. 使用 ref() 而非 reactive()

    // ✅ 正确
    const list = ref([{ id: 1, name: 'Item 1' }])
    // ❌ 错误:reactive() 可能导致拖拽操作静默失败
  2. 列表项必须有唯一稳定的 key

    <!-- ✅ 使用唯一 id -->
    <div v-for="item in list" :key="item.id">
    <!-- ❌ 不要使用 index -->
    <div v-for="(item, index) in list" :key="index">
  3. 选择合适的使用模式

    • 简单场景 → 组件模式 &lt;VueDraggable&gt;
    • 复杂逻辑 / 第三方组件集成 → 组合式函数 useDraggable()
    • 最小模板改动 → 指令模式 v-draggable
  4. 第三方组件集成使用 target

    <!-- Element Plus 表格行拖拽 -->
    <el-table :data="tableData">
      <VueDraggable
        v-model="tableData"
        target=".el-table__body-wrapper tbody"
        :animation="150"
      />
      <el-table-column prop="name" label="Name" />
    </el-table>
  5. 使用 handle 指定拖拽手柄

    <VueDraggable v-model="list" handle=".drag-handle">
      <div v-for="item in list" :key="item.id" class="item">
        <span class="drag-handle">☰</span>
        {{ item.name }}
      </div>
    </VueDraggable>
  6. 使用 filter 排除不可拖拽元素

    <VueDraggable v-model="list" filter=".no-drag">
      <div v-for="item in list" :key="item.id">
        <span class="no-drag"><button>不可拖拽区域</button></span>
        {{ item.name }}
      </div>
    </VueDraggable>
  7. 跨列表拖拽

    <VueDraggable v-model="listA" group="shared">
      <div v-for="item in listA" :key="item.id">{{ item.name }}</div>
    </VueDraggable>
     
    <VueDraggable v-model="listB" group="shared">
      <div v-for="item in listB" :key="item.id">{{ item.name }}</div>
    </VueDraggable>

常见陷阱

陷阱说明解决方案
reactive() 导致响应式失效使用 reactive() 包裹列表,拖拽后数据不更新改用 ref()
index 作为 key排序后渲染错乱使用唯一 id 作为 key
子组件包裹导致拖拽失效&lt;VueDraggable&gt; 放在子组件内不工作使用 target 选项或确保子组件正确暴露根元素
Firefox 新标签页 BugFirefox 下拖拽可能意外打开新标签页(Issue #36设置 draggable: false 在子元素上
指令模式异步数据失败异步加载的数据使用指令模式无效改用组合式函数或组件模式
defineModel 穿透问题v-model 通过 defineModel() 传递时可能不响应(Issue #179避免多层 defineModel 传递拖拽数据
Nuxt SSR 水合不匹配嵌套组件在 Nuxt 中出现 hydration mismatch确保拖拽列表仅在客户端渲染
禁止特定元素拖拽不生效CSS 选择器方式不生效(Issue #233使用 filter + handle 组合方案

技术局限与边界

已知限制

限制说明
依赖 SortableJS 的 DOM 操作模型无法脱离 SortableJS 的底层限制(如大列表性能)
不支持虚拟滚动超大列表(>10,000 项)性能受限
小程序支持不完善有社区讨论但非主要目标
无公开路线图开发以社区 Bug 驱动为主,无正式 Roadmap
封装组件兼容性与高度封装的第三方 UI 组件集成时仍可能遇到 DOM 结构约束
指令模式功能受限异步数据场景不推荐使用指令模式
禁用特定元素拖拽API 不够直观,需组合 filter + handle

不适用场景

  • 超大数据列表(>10,000 项):SortableJS 性能天花板,需自行集成虚拟滚动
  • 小程序拖拽:非主要目标,社区方案不完善
  • 自由拖拽(非排序):应使用 VueUse useDraggable(基于 VueUse 的单元素拖拽)
  • Vue 2.6 及以下:不支持,需使用旧版 vuedraggable

迁移注意事项

从旧版 vuedraggable 迁移到 vue-draggable-plus:

  • API 不完全兼容,需要调整组件用法
  • target 选项可简化与第三方组件的集成
  • 新增组合式函数和指令模式,提供更灵活的使用方式

学习资源

2026 年现状

版本演进

版本关键变更
0.6.1(最新,~2026.01)Bug 修复
0.6.0新增 customUpdate 属性(允许完全接管更新逻辑);改变静态元素处理方式
0.5.x迭代改进和 Bug 修复
0.4.x功能新增
0.3.x早期版本

社区活跃度

  • ~25 万周下载量,在 Vue 拖拽领域处于领先地位
  • GitHub 有 ~102 个 Open Issues 和 ~6 个 Open PR,维护活跃
  • 被 VueUse、Element Plus 社区、Naive UI 社区广泛推荐
  • 2023 年底获得尤雨溪公开推荐,大幅提升知名度

发展趋势

  • Vue 生态拖拽排序的事实标准:已替代旧版 vuedraggable 和 vue.draggable.next
  • 活跃维护:持续追踪 SortableJS 上游更新,修复 Bug
  • 无公开路线图:开发以社区需求驱动
  • Vue 2 + Vue 3 双版本支持仍是核心差异化优势
  • customUpdate 引入:提供更灵活的更新逻辑控制

近期开发重点

  • customUpdate 属性的引入和完善
  • 静态元素处理方案优化
  • forceFallback 最后一个子节点处理
  • DelayOnTouchOnly API 文档修复

选型结论

Vue-Draggable-Plus 是 2026 年 Vue 生态拖拽排序的首选方案。 它基于成熟的 SortableJS,提供组件、组合式函数和指令三种使用方式,完美支持 Vue 3 和 Vue 2.7+。已获得尤雨溪推荐,社区活跃度持续增长。对于 Vue 项目的拖拽排序需求,应优先考虑 vue-draggable-plus。