ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Vue.Draggable 完整使用指南:基于 Sortable.js 的 Vue 2 拖拽组件从入门到进阶

Vue.Draggable 完整使用指南:基于 Sortable.js 的 Vue 2 拖拽组件从入门到进阶 Vue.Draggable 完整使用指南基于 Sortable.js 的 Vue 2 拖拽组件从入门到进阶【免费下载链接】Vue.DraggableVue drag-and-drop component based on Sortable.js项目地址: https://gitcode.com/gh_mirrors/vu/Vue.DraggableVue.Draggable 是运行在 Vue.js 2.0 上的拖拽组件底层基于 Sortable.js让任何列表、表格、手风琴甚至嵌套结构都能获得拖拽排序能力并自动把 DOM 变化同步回 view model 数组。本文以仓库 README.md 为主线结合 核心源码 与 示例目录完整讲解安装、典型用法、全部 Props、事件系统、插槽与常见陷阱帮助你在一篇文章内掌握可复制的实战方案。项目定位与核心特性Vue.Draggable 是一个 Vue 组件Vue 2.0或指令Vue 1.0用于实现拖拽并与 view model 数组保持同步。它基于并完整继承 Sortable.js 的能力包括完整支持 Sortable.js 的功能支持触摸设备支持拖拽手柄drag handles与可选文本智能自动滚动smart auto-scrolling支持在不同列表之间拖拽跨列表拖放无 jQuery 依赖。保持 HTML 与 view model 列表同步兼容 Vue.js 2.0 的transition-group支持取消拖拽操作cancellation事件系统可在需要完全控制时报告任何变更可复用现有 UI 库组件如 vuetify、element、vue material 等通过tag与componentDataProps 让它们变得可拖拽。从源码看组件在mounted钩子中创建 Sortable 实例// src/vuedraggable.js this._sortable new Sortable(this.rootContainer, options); this.computeIndexes();在beforeDestroy钩子中销毁实例src/vuedraggable.js确保组件卸载时不残留事件监听。组件的name为draggable若在浏览器环境检测到全局Vue还会自动注册为全局组件src/vuedraggable.js。注意本仓库对应 Vue 2.0 版本npm 包名为vuedraggable。Vue 3 用户请使用 SortableJS 官方提供的vue.draggable.next项目而vue-draggable带连字符是 Vue 1.0 的包安装时务必区分。安装方式使用 npm 或 yarnyarn add vuedraggablenpm i -S vuedraggable本仓库的 package.json 中依赖为sortablejs: 1.10.2当前版本号为2.24.3package.json。使用 CDN 直接引入script src//cdnjs.cloudflare.com/ajax/libs/vue/2.5.2/vue.min.js/script !-- CDNJS :: Sortable (https://cdnjs.com/) -- script src//cdn.jsdelivr.net/npm/sortablejs1.8.4/Sortable.min.js/script !-- CDNJS :: Vue.Draggable (https://cdnjs.com/) -- script src//cdnjs.cloudflare.com/ajax/libs/Vue.Draggable/2.20.0/vuedraggable.umd.min.js/script完整可运行的示例可以查看仓库的 example 目录其中包含多个独立场景。基本用法Vue 2.0典型用法在模板中直接使用draggable组件通过v-model绑定数组并给每个子元素提供唯一的keydraggable v-modelmyArray grouppeople startdragtrue enddragfalse div v-forelement in myArray :keyelement.id{{element.name}}/div /draggable在.vue文件中注册组件import draggable from vuedraggable // ... export default { components: { draggable, }, // ... }在 example/components/simple.vue 中可以看到完整可运行示例它使用:list绑定数组通过:move校验拖放、用start/end切换拖拽中的状态文案并监听ghost-class等属性。配合 transition-group 使用draggable v-modelmyArray transition-group div v-forelement in myArray :keyelement.id {{element.name}} /div /transition-group /draggable关键约束draggable 组件应当直接包裹可拖拽元素或者包裹一个transition-component即transition-group再由它包裹可拖拽元素。源码中的isTransition函数会检测默认插槽是否恰好只有一个transition-group/TransitionGroup子节点src/vuedraggable.js检测通过后进入transitionMode此时 Sortable 作用于this.$el.children[0]即 transition-group 的实际根节点见rootContainer计算属性 src/vuedraggable.js。使用 footer / header 插槽在draggable元素前后追加“不可拖拽”的内容如添加按钮draggable v-modelmyArray draggable.item div v-forelement in myArray :keyelement.id classitem {{element.name}} /div button slotfooter clickaddPeopleAdd/button /draggabledraggable v-modelmyArray draggable.item div v-forelement in myArray :keyelement.id classitem {{element.name}} /div button slotheader clickaddPeopleAdd/button /draggable注意使用插槽时必须配合draggable.item之类的选择器来标记可拖拽元素。实现上源码在computeChildrenAndOffsets中会把 header 插槽内容拼接到默认插槽之前、footer 插槽内容拼接到默认插槽之后并记录headerOffset/footerOffset拖拽结束时这些偏移量会被加入索引计算src/vuedraggable.js。与 Vuex 集成valueprop 只读因此与 Vuex 天然兼容在 computed 中提供 get/set把 set 转发给 mutationcomputed: { myList: { get() { return this.$store.state.myList }, set(value) { this.$store.commit(updateList, value) } } }模板中直接使用draggable v-modelmyListProps 详解value类型Array必填否默认值null输入给 draggable 组件的数组通常与内部元素v-for引用的数组是同一个。这是官方推荐的使用方式因为它兼容 Vuex。该 prop 不应被直接修改只应通过v-model指令使用draggable v-modelmyArraylist类型Array必填否默认值nullvalueprop 的替代方案。核心区别在于listprop 会被 draggable 组件通过 splice 方法原地修改而value是不可变的。源码中alterList方法体现了这一差异src/vuedraggable.jsalterList(onList) { if (this.list) { onList(this.list); // 直接修改原数组 return; } const newList [...this.value]; // 拷贝后修改 onList(newList); this.$emit(input, newList); // 通过 input 事件回写 }不要与value同时使用。源码在created钩子中做了防御性检查同时传入时会输出错误提示src/vuedraggable.js。所有 Sortable 选项均可作为 Props自版本 2.19 起Sortable 的选项可以直接作为 vue.draggable 的 prop 传入。所有 Sortable 选项都是合法的 prop唯一的例外是所有以 on 开头的方法——draggable 组件通过事件暴露相同的 API。同时支持 kebab-case 写法例如ghost-class会自动转换成 Sortable 的ghostClass选项camelize工具函数实现见 src/util/helper.js。示例设置 handle、sortable 和 group 选项draggable v-modellist handle.handle :group{ name: people, pull: clone, put: false } ghost-classghost :sortfalse changelog !-- -- /draggable源码层面组件的mounted中把所有$attrs键 camelize 后与optionsprop、事件回调合并为 Sortable 构造参数src/vuedraggable.js同时监听options与$attrs的深度变化通过updateOptions动态更新 Sortable 实例只更新非只读属性src/vuedraggable.js。注意源码还默认设置了options.draggable *若未显式提供即默认只有直接子元素可拖拽src/vuedraggable.js。tag类型String默认值divdraggable 组件作为插槽外层元素创建的 HTML 节点类型。也可以传入一个 Vue 组件的名称作为元素此时 draggable 属性会传递给创建出来的组件。若需要给该组件设置 props 或事件请配合componentData使用。组件渲染逻辑见 src/vuedraggable.js最终标签由getTag()tag || element决定。兼容性提示源码中还有一个已废弃的elementprop默认div使用它会输出弃用警告请改用tag见 migrate 文档 中关于 element props 的说明。clone类型Function必填否默认值(original) { return original; }当 clone 选项开启时在源组件上调用此函数来克隆元素。唯一参数是要克隆的 viewModel 元素返回值是其克隆版本。默认情况下 vue.draggable 会复用原 viewModel 元素因此需要克隆或深拷贝时必须使用这个 hook。源码中在拖拽开始时调用this.clone(this.context.element)并把结果挂到evt.item._underlying_vm_上src/vuedraggable.js。在 example/components/clone-on-control.vue 中可以看到按下 Ctrl 键从列表 1 拖到列表 2 时会触发克隆克隆函数为clone({ name }) { // 返回一个新对象实现真正的克隆而非引用复用 }move类型Function必填否默认值null如果非 null此函数会以类似 Sortable onMove 回调的方式被调用。返回false将取消拖拽操作。function onMoveCallback(evt, originalEvent){ ... // return false; — for cancel }evt对象拥有与 Sortable onMove 事件相同的属性并额外附加 3 个属性draggedContext与拖拽元素相关的上下文index被拖拽元素的索引element被拖拽元素对应的 viewModel 元素futureIndex若本次放置被接受被拖拽元素的潜在索引relatedContext与当前拖拽操作目标相关的上下文index目标元素索引element目标元素的 viewModel 元素list目标列表component目标 VueComponentHTML 与 JS 示例禁止拖拽名为 apple 的元素draggable :listlist :movecheckMovecheckMove: function(evt){ return (evt.draggedContext.element.name!apple); }源码中onDragMove会基于moveprop 构造上述两个上下文并计算futureIndexsrc/vuedraggable.jsfutureIndex由computeFutureIndex依据目标列表 DOM 顺序与willInsertAfter计算得出src/vuedraggable.js。类型声明中MoveEvent、DraggedContext、DropContext等结构可在 src/vuedraggable.d.ts 查看。componentData类型Object必填否默认值null用于向tag声明的子组件传递额外信息支持三种键props传递给子组件的 propsattrs传递给子组件的 attrson在子组件上订阅的事件示例结合 element UI 库的el-collapsedraggable tagel-collapse :listlist :component-datagetComponentData() el-collapse-item v-fore in list :titlee.title :namee.name :keye.name div{{e.description}}/div /el-collapse-item /draggablemethods: { handleChange() { console.log(changed); }, inputChanged(value) { this.activeNames value; }, getComponentData() { return { on: { change: this.handleChange, input: this.inputChanged }, attrs:{ wrap: true }, props: { value: this.activeNames } }; } }源码中getComponentAttributes会把componentData中的on、props、attrs合并进渲染节点属性src/vuedraggable.js。事件系统Sortable 事件支持组件支持以下 Sortable 事件start、add、remove、update、end、choose、unchoose、sort、filter、clone。事件在 Sortable.js 触发 onStart、onAdd、onRemove、onUpdate、onEnd、onChoose、onUnchoose、onSort、onClone 时以相同的参数被调用。注意SortableJS 的 onMove 回调被映射为moveprop见上文。源码中Start/Add/Remove/Update/End五个事件会先同步修改内部列表onDragXxx再通过$nextTick异步 emitdelegateAndEmitsrc/vuedraggable.jsChoose/Unchoose/Sort/Filter/Clone则直接 emitsrc/vuedraggable.js。模板示例draggable :listlist endonEndchange 事件当listprop 非空且数组因拖拽操作发生改变时触发change事件。事件带一个参数包含以下属性之一added包含被添加到数组的元素信息newIndex被添加元素的索引element被添加的元素removed包含从数组移除的元素信息oldIndex移除前元素的索引element被移除的元素moved包含数组内部移动的元素信息newIndex移动后元素的当前索引oldIndex移动前元素的索引element被移动的元素这一事件由onDragAdd、onDragRemove、onDragUpdate三个内部方法在完成spliceList/updatePosition等数组操作后触发src/vuedraggable.js。注意value模式下数据回写通过input事件完成而change事件主要面向listprop 模式且change的索引基于可拖拽元素计数header/footer 偏移已扣除。插槽Slots限制header 与 footer 插槽不能与 transition-group 同时使用。Header 插槽使用header插槽在 vuedraggable 组件内部添加不可拖拽的元素。重要应配合draggable选项标记可拖拽元素。无论其在模板中的位置如何header 插槽总是会被添加到默认插槽之前。draggable v-modelmyArray draggable.item div v-forelement in myArray :keyelement.id classitem {{element.name}} /div button slotheader clickaddPeopleAdd/button /draggableFooter 插槽使用footer插槽在 vuedraggable 组件内部添加不可拖拽的元素。重要应配合draggable选项标记可拖拽元素。无论其在模板中的位置如何footer 插槽总是会被添加到默认插槽之后。draggable v-modelmyArray draggable.item div v-forelement in myArray :keyelement.id classitem {{element.name}} /div button slotfooter clickaddPeopleAdd/button /draggable常见陷阱GotchasVue.draggable 的子元素应始终用v-for映射 list 或 value prop。可以使用 header 和 footer 插槽来绕过此限制。v-for内的子元素必须像 Vue.js 中任何元素一样设置 key且需提供有意义的 key 值特别是不要使用数组索引作为 key因为 key 应与条目内容关联克隆出的元素应提供更新后的 key例如通过cloneprop 实现。从实现角度看组件通过「DOM 节点 → vnode → viewModel 元素」的映射维护同步getUnderlyingVm利用computeVmIndex把真实 DOM 元素映射回列表索引src/vuedraggable.js而computeIndexes会基于可见子节点计算visibleIndexes因此 key 的正确性直接影响索引映射的准确性。仓库示例与配套资源本仓库的 example/components 提供了大量可直接运行并对照学习的场景示例文件主题simple.vue基础列表、启停拖拽、ghost 样式handle.vue拖拽手柄handle.handle与输入框共存two-lists.vue两个列表间拖拽grouppeopleclone-on-control.vue按住 Ctrl 克隆拖拽pull函数与clonepropcustom-clone.vue自定义克隆行为transition-example.vue / transition-example-2.vue与 transition-group 结合nested-example.vue嵌套可拖拽树table-example.vue / table-column-example.vue表格场景footerslot.vue / headerslot.vue插槽用法functional.vue函数式组件场景third-party.vue第三方 UI 库组件复用的完整演示示例应用入口在 example/main.js路由配置在 example/route.js本地运行示例可使用npm run serve对应 package.json 中的vue-cli-service serve ./example/main.jspackage.json。单元测试方面tests/unit/vuedraggable.spec.js 覆盖了组件行为tests/unit/vuedraggable.integrated.spec.js 覆盖集成场景tests/unit/vuedraggable.ssr.spec.js 验证服务端渲染场景typeof window ! undefined的守卫保证了 SSR 安全测试辅助组件在 tests/unit/helper 下。关于 Vue 1.0Vue 1.0 的使用方式见仓库文档 Vue.draggable.for.ReadME.md从旧 API 迁移到新 API 的说明见 migrate.md例如elementprop 与optionsprop 的弃用与替代方案。本文全部内容针对当前仓库对应的 Vue 2.0 组件版本使用时请确保 Vue 主版本匹配。【免费下载链接】Vue.DraggableVue drag-and-drop component based on Sortable.js项目地址: https://gitcode.com/gh_mirrors/vu/Vue.Draggable创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进