Vue.Draggable 迁移指南:从 element / options 旧 API 平滑升级到 tag 与 Sortable 配置直传
2026/9/20 8:31:19 网站建设 项目流程

Vue.Draggable 迁移指南:从 element / options 旧 API 平滑升级到 tag 与 Sortable 配置直传

【免费下载链接】Vue.DraggableVue drag-and-drop component based on Sortable.js项目地址: https://gitcode.com/gh_mirrors/vu/Vue.Draggable

导读

Vue.Draggable(基于 Sortable.js 的 Vue 2.0 拖拽组件)在 v2.19 / v2.20 两个版本中先后引入了两处破坏性 API 调整:element属性被废弃并推荐改用tagoptions属性被废弃并推荐将 Sortable 配置直接以 props 形式挂载到<draggable>上。本文以官方迁移文档 documentation/migrate.md 为核心骨架,结合仓库源码(src/vuedraggable.js)与单元测试(tests/unit/vuedraggable.spec.js)逐条解读迁移原因、改写示例与底层原理,帮助你完成升级后代码可读性更强、类型更清晰、与 Vue 组件体系完全对齐。

迁移背景:为什么需要升级

本次迁移涉及两个被标记为 deprecated 的属性,它们分别对应组件的两种职责:

废弃属性替代方案引入版本动机
elementtag2.x 早期(官方建议迁移)与业界广泛使用的命名约定保持一致
options直接 props /v-bindv2.20 起废弃(v2.19 已支持直传)借助 Vue 透明包装器(transparent wrapper)机制,把 Sortable 配置当作普通 props 透传

需要说明的是,废弃不等于立即移除:源码中这两个 props 仍然保留并兼容工作,但会在控制台输出弃用警告(详见下文"源码佐证")。仓库中 documentation/legacy.options.md 同样记录了options的弃用说明,可作为交叉参考。

一、elementtag迁移

1.1 迁移示例

element被废弃后,应当改用tag属性,二者的语义完全一致:指定 draggable 组件作为外层包裹元素的 HTML 节点类型。

迁移前:

<draggable v-for="list" element="ul"> <!-- --> </draggable>

迁移后:

<draggable v-for="list" tag="ul"> <!-- --> </draggable>

1.2 源码佐证:element 与 tag 的底层关系

在 src/vuedraggable.js 中,两个 props 的定义如下:

element: { type: String, default: "div" }, tag: { type: String, default: null },

getTag()方法的实现说明tag拥有更高优先级,element只是历史兜底:

getTag() { return this.tag || this.element; }

也就是说,只要设置了tagelement的值就会被完全忽略;两者都不设置时,默认渲染为div(README 中tag的默认值说明为'div',与源码默认行为一致)。

1.3 弃用警告的触发条件与验证

在组件created生命周期中(src/vuedraggable.js),只要检测到element不是默认值"div",就会打印弃用警告:

if (this.element !== "div") { console.warn( "Element props is deprecated please use tag props instead. See ...migrate.md#element-props" ); }

注意:如果你从未使用过element(保持默认"div"),不会触发警告;只有显式传入element="ul"之类的非默认值才会提示。这一行为已被单元测试锁定,见 tests/unit/vuedraggable.spec.js:测试断言当propsData传入element: "li"时,console.warn会被调用,并携带指向本文档(migrate.md#element-props)的提示文案。

1.4 扩展:tag的进阶用法

迁移到tag后,其能力比旧的element更完整——除了普通的 HTML 标签名(ulspandivtable等),还可以传入Vue 组件名作为外层元素:

  • tag为组件名时,draggable 组件会把相关 attribute 传递给被创建的组件;
  • 若需要向该组件传 props、attrs 或事件监听,请配合componentData属性(参见 README 中的 componentData 说明)。

源码中getComponentAttributes(src/vuedraggable.js)负责在渲染阶段将iddata-*属性以及componentData中的on/props/attrs合并到外层元素上,这也是tag支持组件化外层容器的底层机制。仓库中的测试覆盖了ulspandiv三种标签的根元素渲染,以及tag: "child"组件模式的 props / 事件 / 属性传递(见 tests/unit/vuedraggable.spec.js)。

二、options→ 直接 props /v-bind迁移

2.1 背景:v2.20 的透明包装器机制

options属性在 v2.20 版本被标记废弃。从该版本开始,Vue.draggable 采用透明包装器(transparent wrapper)模式:所有 Sortable 配置项都可以直接作为属性挂载到<draggable>实例上,组件内部会把这些属性透传给底层的 Sortable 实例。这省去了手动书写:options="{...}"的冗余嵌套,也让模板声明式地表达拖拽行为。

2.2 迁移示例一:静态配置对象

迁移前,拖拽手柄需要包一层对象:

<draggable v-for="list" :options="{handle: '.handle'}"> <!-- --> </draggable>

迁移后,直接把 Sortable 的handle选项写为属性即可:

<draggable v-for="list" handle=".handle"> <!-- --> </draggable>

2.3 迁移示例二:动态配置对象

迁移前,通过方法返回配置对象再绑定:

<draggable v-for="list" :options="getOptions()"> <!-- --> </draggable>

迁移后,使用 Vue 内置指令v-bind(不带参数)将对象展开为多个 props:

<draggable v-for="list" v-bind="getOptions()"> <!-- --> </draggable>

这是两种合法的书写形态:

  • 字面量场景:直接在模板上写handle=".handle"ghost-class="ghost"等;
  • 动态/批量场景:用v-bind="getOptions()"把返回的配置对象一次性展开,等价于把对象中的每个键作为 props 传入。

2.4 源码佐证:$attrs 是如何变成 Sortable 选项的

这一机制的核心在组件挂载阶段(src/vuedraggable.js)。组件声明了inheritAttrs: false(src/vuedraggable.js),因此未被 props 声明的属性不会自动落到根元素上,而是进入this.$attrs,随后在mounted中被收集、驼峰化并合并进 Sortable 配置:

const attributes = Object.keys(this.$attrs).reduce((res, key) => { res[camelize(key)] = this.$attrs[key]; return res; }, {}); const options = Object.assign({}, this.options, attributes, optionsAdded, { onMove: (evt, originalEvent) => { return this.onDragMove(evt, originalEvent); } });

这里有两个值得注意的实现细节:

  1. 键名自动驼峰化camelize工具函数(src/util/helper.js)会把ghost-class这类 kebab-case 写法转换为 Sortable 期望的ghostClass。README 也明确说明:"kebab-case properties are supported",例如ghost-classprops 会被转换为ghostClasssortable option。
  2. 兼容性合并Object.assign({}, this.options, attributes, ...)意味着旧的options对象、新直传的$attrs、内部事件代理会按优先级依次合并,后者覆盖前者——因此即使同时存在两种写法,行为也有明确预期。

此外,mounted 末尾还会设置默认值(src/vuedraggable.js):

!("draggable" in options) && (options.draggable = ">*");

即未显式声明draggable选项时,默认只允许直接子元素参与拖拽。

2.5 弃用警告与动态更新

element一样,created钩子中检测到options被使用时同样会打印警告(src/vuedraggable.js):

if (this.options !== undefined) { console.warn( "Options props is deprecated, add sortable options directly as vue.draggable item, or use v-bind. See ...migrate.md#options-props" ); }

对应的测试见 tests/unit/vuedraggable.spec.js:传入options: { group: "led zeppelin" }时断言console.warn被调用。

另一个重要能力是响应式更新:组件对$attrs设置了深度 watch(src/vuedraggable.js),当属性变化时会调用updateOptions(src/vuedraggable.js),把每个驼峰化后的新值通过this._sortable.option(name, value)动态写入 Sortable 实例,实现不重建实例的实时配置更新。单元测试通过 mock Sortable 断言了toBeCamelized: true的透传结果(tests/unit/vuedraggable.spec.js)。

三、迁移注意事项与边界

3.1 以on开头的方法选项不能直传

不是所有 Sortable 选项都适合以 props 直传。组件内部定义了只读属性列表(src/vuedraggable.js):

const readonlyProperties = ["Move", ...eventsListened, ...eventsToEmit].map( evt => "on" + evt );

onStartonAddonRemoveonUpdateonEndonChooseonUnchooseonSortonFilteronCloneonMove这些回调无法通过 props 透传(updateOptions会跳过它们),因为 Vue.draggable 已经将这些回调映射为同名事件对外暴露(startaddremoveupdateendchooseunchoosesortfilterclone),onMove则映射为move属性。迁移时请改用事件监听:

<draggable :list="list" @end="onEnd" @change="onChange"> </draggable>

3.2 与内置 props 的区分

直接透传的对象中,如果包含与组件内置 props 同名的键(如listvaluetagmoveclonecomponentDatanoTransitionOnDrag),它们会被 Vue 的 props 声明捕获,不会进入$attrs参与 Sortable 配置——这是透明包装机制与组件自身 API 之间天然的隔离边界,使用v-bind="getOptions()"时建议确保配置对象里只含 Sortable 选项。

3.3 新旧写法并存期间的排查手段

升级过程中,可以依赖两个信号判断代码是否已迁移干净:

  1. 控制台警告:任一<draggable>传入非默认element或传入了options,控制台都会打印指向本迁移文档的警告文案(见 1.3、2.5 节);
  2. 测试断言:仓库单元测试对这两条警告文案做了精确断言,如果你维护自己的组件测试,可以仿照 tests/unit/vuedraggable.spec.js 的方式,在测试中 spyconsole.warn并断言警告不再出现,从而自动化地验证迁移完成度。

3.4 综合迁移示例

结合官方文档与 README 中的完整配置示例,迁移后的典型写法如下(README.md):

<draggable v-model="list" tag="ul" handle=".handle" :group="{ name: 'people', pull: 'clone', put: false }" ghost-class="ghost" :sort="false" @change="log" > <!-- --> </draggable>
  • tag="ul"替代旧的element="ul"
  • handlegroupghost-classsort直接作为 props 替代旧的:options对象写法;
  • 事件(如@change)保持与旧写法一致。

仓库中 tests/unit/helper/DraggableWithList.vue 与 tests/unit/helper/DraggableWithModel.vue 展示了tag="span"v-model组合的实测用法,可作为升级后的参照模板。

结语

本次迁移的本质,是把 Vue.draggable 从一个"包装了配置对象的组件"演进为"对 Sortable 全透明、可直传配置的组件":element改为tag对齐了 Vue 生态通用的命名习惯,options改为直接 props /v-bind则消除了模板中的一层对象嵌套,并借助$attrs驼峰化、v-bind展开与_sortable.option动态更新机制,让 Sortable 的全部配置都能以声明式、响应式的方式接入 Vue 模板体系。理解 src/vuedraggable.js 中getTag()created警告、mounted阶段的Object.assign合并逻辑,以及 src/util/helper.js 的camelize实现,就能在升级时从容判断每一处改动的影响边界,让迁移过程既快又稳。

【免费下载链接】Vue.DraggableVue drag-and-drop component based on Sortable.js项目地址: https://gitcode.com/gh_mirrors/vu/Vue.Draggable

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询