1、安装插件
Vue2
npm i vuedraggable@2 # 或 yarn add vuedraggable
Vue3(官方仓库改名 vue.draggable.next)
npm i vuedraggable@next # 或 yarn add vuedraggable@next
CDN(测试用)
cdn.jsdelivr.net/npm/vuedrag...
cdn.jsdelivr.net/npm/vuedrag...
2、注册组件
全局注册(main.js)
import draggable from 'vuedraggable' // Vue3 把包名改成 'vuedraggable-next'
app.component('draggable', draggable)
局部注册
import draggable from 'vuedraggable'
export default { components:{ draggable } }
3、最小可运行示例
Vue2 写法
js
<template>
<draggable v-model="list" class="drag-area">
<div v-for="item in list" :key="item.id">{{ item.name }}</div>
</draggable>
</template>
<script>
import draggable from 'vuedraggable'
export default {
components:{ draggable },
data(){ return { list:[{id:1,name:'A'},{id:2,name:'B'}] } }
}
</script>
Vue3 写法(必须具名插槽 item,且只能一个根节点)
vue
<template>
<draggable v-model="list" item-key="id">
<template #item="{ element }">
<div class="drag-area__item">{{ element.name }}</div>
</template>
</draggable>
</template>
<script setup>
import draggable from 'vuedraggable-next'
import { reactive } from 'vue'
const list = reactive([ {id:1,name:'A'}, {id:2,name:'B'} ])
</script>
4、核心 Props 详解
属性 | 类型 | 默认值 | 说明 |
---|---|---|---|
v-model / list | Array | [] | 与视图同步的数据源;list 与 v-model 不可同时出现 |
item-key | String | 无 | Vue3 必填,提供列表 key |
tag | String | 'div' | 容器节点名 |
handle | String | '' | 仅当匹配选择器时才允许拖动,如 handle=".drag-handle" |
group | String / Object | '' | 跨列表拖拽,如 group="shared" 或 :group="{name:'shared', pull:'clone', put:true}" |
animation | Number | 150 | 排序动画时长(ms) |
ghost-class | String | 'sortable-ghost' | 占位元素样式 |
drag-class | String | 'sortable-drag' | 正在拖拽元素样式 |
filter | String | '' | 禁止拖动的选择器 |
disabled | Boolean | false | 禁用拖拽 |
move | Function | null | 返回 false 取消本次拖动 |
clone | Function | original=>original | 跨列表 pull='clone' 时,控制克隆逻辑 |
5、事件
事件名 | 回调形参 | 触发时机 |
---|---|---|
@start | evt | 开始拖动 |
@end | evt | 拖动结束 |
@change | evt | 列表数据发生 add / remove / move 时触发 |
@add | evt | 接收外部元素 |
@remove | evt | 元素被拖到其他列表 |
@update | evt | 同列表内部排序 |
@choose / unchoose | evt | 选中/取消选中 |
6、多列表互相拖拽
A、B两个列表可互相拖入,支持克隆:
vue
<template>
<div class="lists">
<!-- 列表 A -->
<draggable
v-model="listA"
:group="{ name:'shared', pull:'clone', put:true }"
:clone="deepClone"
>
<template #item="{ element }"><div>{{ element.name }}</div></template>
</draggable>
<!-- 列表 B -->
<draggable
v-model="listB"
group="shared"
>
<template #item="{ element }"><div>{{ element.name }}</div></template>
</draggable>
</div>
</template>
<script setup>
import { reactive } from 'vue'
import draggable from 'vuedraggable-next'
const listA = reactive([{id:1, name:'a'}])
const listB = reactive([])
const deepClone = (o) => JSON.parse(JSON.stringify(o))
</script>
注意点:
- group 的 name 必须相同;
- 数据结构一致;
- 数组必须是响应式(Vue2 用 Vue.observable / Vue3 用 reactive、ref)。
7、插槽用法
插槽名 | 作用域 | 说明 |
---|---|---|
default | - | 整个容器内容,可放置不可拖动的 header/footer |
item | { element, index } | Vue3 唯一必填插槽,渲染每一个可拖拽节点 |
header/footer | - | 固定区域,不会参与拖拽 |
示例:在列表顶部增加不可拖动的「添加按钮」
vue
<draggable v-model="list">
<template #header>
<button @click="add">+ Add</button>
</template>
<template #item="{ element }">
<div>{{ element.name }}</div>
</template>
</draggable>
8、与UI库混合使用
Element Plus / Ant Design Vue 的表格、卡片、步骤条等均可被 draggable 包裹:
vue
<draggable v-model="tableData" item-key="id">
<template #item="{ element }">
<el-card>{{ element.title }}</el-card>
</template>
</draggable>
9、常见问题
- Vue3 报错 "item slot required" → 使用具名插槽
#item
。 - 拖动后视图未更新 → 确认数组是响应式 & key 唯一。
- 两个列表无法互相拖 → group name 不一致或数据结构不同。
- 想拖拽表格行 → 使用
<tr>
作为根节点即可(需设置tag="tbody"
)。
10、在线资源/官方仓库
- Vue2 版(稳定):github.com/SortableJS/...
- Vue3 版: github.com/SortableJS/...
官方 Sortable.js 配置表(拖拽行为全部继承):github.com/SortableJS/...