使用 SortableJS 实现拖拽排序
前言
后台管理、看板、表单设计器等场景里,经常需要让用户通过拖拽调整列表顺序。
自己监听 mousedown / mousemove 再操作 DOM,代码量大,还要处理触摸端、动画和边界情况。
SortableJS 是一个轻量的原生 JS 拖拽排序库,不依赖 jQuery 或 Vue,几行代码就能让列表支持拖拽重排。
本文介绍安装方式、基础用法、与数据数组同步,以及 Vue 3 项目中的常见集成思路。
下文示例以浏览器环境为主,版本号可按项目实际调整。
依赖
SortableJS 可通过 npm 安装,也可在静态页面里直接用 CDN 引入。
npm
下面命令将 SortableJS 安装为项目依赖。
1 | npm install sortablejs |
CDN
若只是快速验证或在无构建工具的项目中使用,可在 HTML 中引入 CDN 脚本。
1 | <script src="https://cdn.jsdelivr.net/npm/sortablejs@1.15.6/Sortable.min.js"></script> |
实现
HTML 结构
SortableJS 作用于一个容器元素,容器内的直接子元素才是可拖拽项。
下面是一个最简单的列表示例。
1 | <ul id="sortable-list"> |
每个 <li> 对应一条可排序项,容器 #sortable-list 将作为 Sortable 的挂载目标。
基础初始化
引入 Sortable 后,对容器调用 Sortable.create 即可启用拖拽排序。
1 | import Sortable from 'sortablejs' |
animation 控制换位时的过渡动画时长(毫秒)。ghostClass 是拖拽过程中占位元素附加的 CSS 类名,便于自定义半透明样式。
配合占位样式,拖拽时的视觉反馈会更清晰。
1 | .sortable-ghost { |
读取排序结果
拖拽结束后,DOM 顺序已经改变,但业务数据往往还保存在 JS 数组里。
可在 onEnd 回调里按当前 DOM 顺序重建数组。
1 | let items = [ |
onEnd 在拖拽完成时触发,此时可根据子元素的 data-id 重新排列 items 数组,再提交给后端或写入本地状态。
常用配置
SortableJS 提供了丰富的选项,下面几个在业务里最常见。
1 | Sortable.create(listEl, { |
handle 适合表格行、卡片等「整行可点但只想让图标区域可拖」的场景。group 可用于看板列之间拖动任务卡片。evt.oldIndex 与 evt.newIndex 可直接用于数组的 splice 重排,不必再遍历 DOM。
若已有数组,用索引更新往往更简洁。
1 | function moveItem(arr, from, to) { |
销毁实例
组件卸载或不再需要排序时,应调用 destroy 释放事件监听,避免内存泄漏。
1 | const sortable = Sortable.create(listEl, { animation: 150 }) |
验证
本地起一个静态页面或 dev server,打开浏览器控制台即可验证。
- 用鼠标按住列表项上下拖动,观察 DOM 顺序是否变化。
- 在
onEnd里console.log输出数组,确认与界面顺序一致。 - 若使用了
handle,点击非手柄区域应无法拖动。
Chrome DevTools 的 Elements 面板也可实时查看 <li> 节点顺序是否与预期一致。
扩展
Vue 3 集成
在 Vue 3 中不必手写 DOM 同步,常用做法是使用基于 SortableJS 封装的 vuedraggable(包名 vuedraggable,Vue 3 对应 v4 版本)。
先安装依赖。
1 | npm install sortablejs vuedraggable@next |
模板里用 v-model 绑定数组,拖拽会自动更新数据顺序。
1 | <template> |
若项目约定「Vue 文件不写 TS」,可将上述脚本放到同名 .js 文件中再 import 进 .vue。
多列表互相拖入
看板场景下,给多个列表容器设置相同的 group 名称,即可在同一组内跨列拖动。
1 | const groupName = 'kanban' |
跨列拖动时,每个列表维护各自的数组,在 onAdd / onRemove 回调里同步数据即可。
总结
SortableJS 适合快速为列表、表格行、看板卡片添加拖拽排序能力。
核心步骤是:准备容器与子项 DOM → Sortable.create 初始化 → 在 onEnd 中同步业务数组 → 不用时 destroy。
需要与 Vue 响应式深度结合时,优先考虑 vuedraggable,减少手写 DOM 与数组双向同步的代码。
配置 handle、filter、group 等选项即可覆盖大多数产品交互,无需从零实现拖拽逻辑。