首页 / 资讯中心 / 文章详情

VueUse useSortable 实战指南:在 Vue 3 中轻松实现列表拖拽排序

VueUse useSortable 实战指南:在 Vue 3 中轻松实现列表拖拽排序 ★ FEATURED ARTICLE
前端【免费下载链接】vueuseCollection of essential Vue Composition Utilities for Vue 3项目地址https://gitcode.com/gh_mirrors/vu/vueuse点击查看免费下载导读useSortable是 VueUsevueuse/integrations包中针对 SortableJS 的响应式封装它让开发者无需手动管理 Sortable 实例的生命周期即可在 Vue 3 组件中快速实现列表的拖拽排序同时保持拖拽结果与响应式数据源的双向同步。阅读本文后你将掌握useSortable的安装、四种主流使用姿势模板引用、CSS 选择器、组件模式、返回方法start/stop/option的运行时用法、watchElement自动重初始化策略以及如何通过自定义onUpdate处理器精细控制数据变更逻辑。是什么一个薄而完整的 SortableJS 响应式封装useSortable定位简单明确它是 SortableJS 的包装器Wrapper。它并不重新发明拖拽算法而是把 SortableJS 的实例创建、销毁、选项读取与修改以及拖拽结束后的数组重排全部收敛为符合 VueUse 风格的可组合函数让使用者拿到的是干净的返回值接口。从源码结构看packages/integrations/useSortable/index.ts整个模块只依赖三样东西sortablejs作为底层拖拽引擎VueUse 自身的工具函数tryOnMounted、tryOnScopeDispose、unrefElement、defaultDocument等负责生命周期与元素解析Vue 的响应式 APIisRef、nextTick、toValue、watch负责数据同步。它在 packages/integrations/index.ts 中被统一导出属于 VueUse 的「集成层」vueuse/integrations同层还有useAxios、useDrauu、useQRCode等一批第三方库封装。官方文档packages/integrations/README.md建议为了更好的 tree-shaking 效果优先从子模块路径导入例如import { useSortable } from vueuse/integrations/useSortable而非从主入口vueuse/integrations整体导入。需要特别留意的一点限制目前useSortable只支持单列表内部的拖拽排序跨列表拖拽多列表之间的移动尚未实现规划功能时应预先评估该边界。安装两个依赖缺一不可useSortable是一个可选集成SortableJS 被声明为peerDependenciesMeta中的可选依赖见 packages/integrations/package.json因此需要手动安装底层库npm i vueuse/integrations sortablejs^1如果你的项目使用 pnpm等价命令为pnpm add vueuse/integrations sortablejs^1注意版本约束SortableJS 需要^1主版本系列types/sortablejs同步提供类型支持Vue 版本要求^3.5.0。若只想按需安装useSortable而不引入整个 integrations 包也可以直接安装后从子模块路径导入——仓库的 exports 映射packages/integrations/package.json 中的./useSortable与./useSortable/component条目保证了这种按需导入在打包时是安全的。四种使用方式与底层原理方式一模板引用Template Ref——最推荐script setup langts import { useSortable } from vueuse/integrations/useSortable import { shallowRef, useTemplateRef } from vue const el useTemplateRef(el) const list shallowRef([{ id: 1, name: a }, { id: 2, name: b }, { id: 3, name: c }]) useSortable(el, list) /script template div refel div v-foritem in list :keyitem.id {{ item.name }} /div /div /template这是最贴近 Vue 惯例的用法el是模板引用list是数据源。从实现上看useSortable在内部通过tryOnMounted(start)挂载时自动初始化 Sortable 实例packages/integrations/useSortable/index.ts并且默认内置了onUpdate处理器每当拖拽结束它自动调用moveArrayElement(list, e.oldIndex!, e.newIndex!, e)将数组重排回响应式数据index.ts。也就是说拖拽完成后list会直接反映新的顺序无需编写任何同步代码。方式二指定拖拽手柄Handle 运行时动态配置选项script setup langts import { useSortable } from vueuse/integrations/useSortable import { shallowRef, useTemplateRef } from vue const el useTemplateRef(el) const list shallowRef([{ id: 1, name: a }, { id: 2, name: b }, { id: 3, name: c }]) const animation 200 const { option } useSortable(el, list, { handle: .handle, }) option(animation, animation) // 运行时设置 // const v option(animation) // 运行时读取返回 200 /script template div refel div v-foritem in list :keyitem.id span{{ item.name }}/span span classhandle*/span /div /div /template这里演示了两个核心技巧handle选项只有点击.handle元素才能触发拖拽常用于「列表项内部只有特定区域可拖」的场景option方法在实例创建之后动态设置/读取任意 Sortable 选项。其底层实现index.ts是转发给 SortableJS 实例的sortable.option(name, value)/sortable.option(name)传入value时是设置返回void省略value时是读取返回当前值。由于option通过重载签名约束了name必须是Sortable.Options的合法键见类型声明UseSortableReturn运行时的笔误在编译期就会被拦截。仓库自带的可交互演示packages/integrations/useSortable/demo.vue正是利用这一机制用两个按钮在animation: 150与animation: 0之间切换排序动画的开与关。方式三CSS 选择器定位根元素script setup langts import { useSortable } from vueuse/integrations/useSortable import { shallowRef } from vue const list shallowRef([{ id: 1, name: a }, { id: 2, name: b }, { id: 3, name: c }]) useSortable(#dv, list) /script template div iddv div v-foritem in list :keyitem.id span{{ item.name }}/span /div /div /template当容器元素不在当前组件作用域内例如位于其他组件或动态注入的 DOM 中时可以直接传 CSS 选择器字符串。源码中字符串分支通过document?.querySelector(el)解析目标元素index.ts且document默认取defaultDocument同时接受ConfigurableDocument选项以便在非浏览器环境注入自定义 document 对象。注意事项字符串分支不会触发watchElement的元素监听源码只在typeof el ! string时才建立 watch见 index.ts。如果目标元素是v-if条件渲染、在挂载后才出现的start()每次调用都会重新querySelector查一次 DOM——测试用例packages/integrations/useSortable/index.browser.test.ts 中的string selector regression分组专门钉住了这一行为目标元素出现后再调用start()或stop()start()都能正确绑定 Sortable。这是挂载时查询失败后的标准自救手段。方式四组件式UseSortablescript setup langts import { UseSortable } from vueuse/integrations/useSortable/component import { shallowRef } from vue const list shallowRef([ { id: 1, name: a }, { id: 2, name: b }, { id: 3, name: c }, ]) /script template UseSortable v-modellist asol :options{ animation: 150 } li v-foritem in list :keyitem.id {{ item.name }} /li /UseSortable /template对于偏好声明式模板的团队可以引入同名组件UseSortable子路径vueuse/integrations/useSortable/component在 packages/integrations/tsdown.config.ts 构建流程下与函数式实现共享同一份核心逻辑。该组件的核心机制packages/integrations/useSortable/component.ts通过useVModel(props, modelValue)建立v-model双向绑定拖拽结果直接写回外部list通过as属性自定义渲染标签默认div示例中为ol子元素由默认插槽提供内部用shallowRef持有目标元素并将useSortable的返回值放入reactive后暴露给插槽作用域。插槽作用域可以拿到start、stop、option等控制方法从而实现「停用/恢复排序」按钮template UseSortable v-slot{ stop, start } v-modellist button clickstop()Stop Sorting/button button clickstart()Start Sorting/button div v-foritem in list :keyitem.id {{ item.name }} /div /UseSortable /template返回值start / stop / option 的完整生命周期属性说明start初始化 Sortable 实例挂载时自动调用stop销毁 Sortable 实例option运行时获取或设置 Sortable 选项const { start, stop, option } useSortable(el, list) // 停用排序 stop() // 重新启用排序 start() // 读写选项 option(animation, 200) // 设置 const animation option(animation) // 读取结合源码理解其生命周期模型start会先解析目标元素字符串走querySelector其余走unrefElement再创建new Sortable(target, {...defaultOptions, ...resetOptions})。这里有个值得注意的实现细节用户传入的options会与内置的默认onUpdate合并因此自定义onUpdate会覆盖默认的数据同步逻辑——这正是下一节「自定义更新处理器」的入口。stop与组件卸载时的tryOnScopeDispose钩子都会执行cleanup()即sortable?.destroy()并清空实例引用避免内存泄漏index.ts。当watchElement为false默认时Sortable 只在挂载时初始化一次元素引用变化后需要手动start()。测试用例packages/integrations/useSortable/index.browser.test.ts为这三个方法提供了直接验证stop()后Sortable.get(el)返回nullstart()后实例重新出现option(disabled, true)能正确写入并被读取。这些行为与文档描述一一对应。watchElement让 Sortable 跟随元素变化自动重建当容器元素本身是条件渲染如v-if切换时默认策略会让 Sortable 实例停留在旧的可能已卸载的元素上。此时开启watchElement即可自动跟随import { useSortable } from vueuse/integrations/useSortable useSortable(el, list, { watchElement: true, // 元素变化时自动重新初始化 })源码实现index.ts在watchElement: true且目标为元素引用非字符串时会用watch(() unrefElement(el), ..., { immediate: true, flush: post })监听元素引用元素变化时先cleanup()销毁旧实例再对新元素initSortable重新初始化immediate: true保证组件一挂载就完成首轮初始化。watchElement的行为差异在测试中有非常直观的对照index.browser.test.ts 的watchElement分组开启时v-if从true切到false再切回true新元素上的 Sortable 会被自动重新初始化关闭时默认新元素上没有 Sortable旧实例仍绑定在已移除的元素上必须手动stop()start()才能在新元素上重建实例。因此凡涉及条件渲染、动态挂载/卸载容器的场景优先开启watchElement: true可以省去手动管理实例的繁琐工作。自定义更新处理器接管数据移动逻辑默认情况下useSortable内置了onUpdate拖拽完成后自动重排数组。若你需要在上报数据前做额外处理例如记录操作日志、触发异步请求、做位置校验可以传入自定义onUpdate——此时默认处理器被覆盖数组移动需要你自行调用暴露的moveArrayElement完成import { moveArrayElement, useSortable } from vueuse/integrations/useSortable useSortable(el, list, { onUpdate: (e) { // 自定义业务逻辑 moveArrayElement(list, e.oldIndex, e.newIndex, e) // moveArrayElement 在微任务中执行因此这里需要 nextTick // 等待其完成后再继续 nextTick(() { /* do something */ }) } })这里的关键细节是时序moveArrayElement对ref类型的数组采用「先浅拷贝、再在nextTick中完成splice写入」的策略index.ts以避免移动元素时反复触发副作用。因此文档明确提示如果你需要在重排完成后立即读取最新数组请放在nextTick回调中执行。辅助函数可单独使用的工具useSortable模块还导出了三个独立辅助函数均有完整类型声明与源码实现函数说明moveArrayElement(list, from, to, event?)将数组中的元素从from索引移动到to索引insertNodeAt(parent, element, index)在parent的指定索引位置插入一个 DOM 节点removeNode(node)从父节点中移除一个 DOM 节点它们的设计意图与moveArrayElement的调用方式互相呼应moveArrayElement在传入event参数时会先通过removeNode(e.item)把被拖拽的真实 DOM 节点从原位置移除再通过insertNodeAt(e.from, e.item, from)按原索引插回父节点从而保持 DOM 结构稳定之后再异步完成数组索引的移动index.tsinsertNodeAt基于parentElement.children[index]定位参照节点用insertBefore完成插入索引越界时即退化为追加到末尾removeNode在节点存在父节点时执行removeChild带空指针保护。三者组合使用即可在没有 Sortable 的情况下手工编排 DOM 与数组的同步移动。类型声明速览useSortable对外暴露的完整类型与源码中的UseSortableReturn、UseSortableOptions一一对应见 index.tsUseSortableReturn包含start(): void、stop(): void以及重载的optionK extends keyof Sortable.Options(name, value)设置与optionK(name)读取双形态UseSortableOptions继承Sortable.Options与ConfigurableDocument额外提供watchElement?: boolean默认false——即默认只在挂载时初始化一次函数重载useSortable同时接受selector: string与el: MaybeRefOrGetterMaybeElement两种首参形态list则统一为MaybeRefT[]普通数组或响应式 ref 均可。总结useSortable把 SortableJS 的实例管理与 Vue 的响应式数据桥接压缩成了一个可组合函数默认的onUpdate自动完成数组重排option()提供运行时的选项读写watchElement处理条件渲染下的实例重建而moveArrayElement/insertNodeAt/removeNode三个辅助函数则让精细控制成为可能。结合 index.ts 的源码与 index.browser.test.ts 的测试覆盖你可以放心地在生产项目中使用它实现单列表拖拽排序至于跨列表拖拽则需等待后续版本或自行扩展。赞分享前端【免费下载链接】vueuseCollection of essential Vue Composition Utilities for Vue 3项目地址https://gitcode.com/gh_mirrors/vu/vueuse点击查看免费下载相关推荐VueUse useSortable 实战指南在 Vue 3 中优雅集成 SortableJS 拖拽排序VueUse useSortable 实战指南在 Vue 3 中优雅集成 SortableJS 拖拽排序 useSortable 是 VueUse 对 Sor前端在 airi 的 Vue 3 应用中用 VueUse useSortable 实现拖拽排序完整实战与源码级解读在 airi 的 Vue 3 应用中用 VueUse useSortable 实现拖拽排序完整实战与源码级解读 useSortable 是 VueUse 对AI 应用人工智能大模型数字人AI Agent语音前端后端桌面应用移动开发即时通讯3D渲染FGO-py告别重复操作让《命运/冠位指定》智能自动化的终极方案FGO py告别重复操作让《命运/冠位指定》智能自动化的终极方案 还在为《命运/冠位指定》FGO中无尽的刷本、抽卡、日常任务而烦恼吗每天花费数小时在重GUI 自动化桌面应用计算机视觉RPA任务调度上一篇Diablo Edit2终极指南打造完美暗黑破坏神2角色的完整解决方案下一篇终极网盘直链下载助手完整指南5分钟告别限速一键获取真实下载链接创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
阅读完成 · 觉得有帮助?
咨询建站