《Vue3 Composition API 实战:开发一个支持拖拽排序的列表组件》
·
Vue3 Composition API 实战:开发一个支持拖拽排序的列表组件
本文将一步步指导你使用 Vue3 的 Composition API 开发一个支持拖拽排序的列表组件。Composition API 提供了更灵活的逻辑复用方式,而拖拽排序功能在任务管理、图片库等场景中非常实用。教程结构清晰,分为六个步骤,每个步骤包含详细解释和代码片段。最终,你将得到一个完整的可运行组件。
步骤 1: 准备工作
确保你的开发环境已安装 Vue3。如果使用 Vue CLI 或 Vite 创建项目,安装依赖:
npm install vue@3
我们将使用原生 HTML5 拖拽 API,无需额外库,保持轻量。
步骤 2: 组件基础结构
创建 Vue 单文件组件(如 DraggableList.vue)。使用 Composition API 的 setup 函数定义响应式数据和基本模板:
<template>
<ul class="list">
<li
v-for="(item, index) in items"
:key="item.id"
class="list-item"
draggable="true"
@dragstart="handleDragStart(index)"
@dragover="handleDragOver"
@drop="handleDrop(index)"
>
{{ item.name }}
</li>
</ul>
</template>
<script>
import { ref } from 'vue';
export default {
setup() {
// 响应式列表数据,初始示例数据
const items = ref([
{ id: 1, name: '项目 A' },
{ id: 2, name: '项目 B' },
{ id: 3, name: '项目 C' }
]);
// 存储拖拽源索引
const draggedIndex = ref(null);
return { items, draggedIndex };
}
};
</script>
<style scoped>
.list {
list-style: none;
padding: 0;
}
.list-item {
padding: 10px;
margin: 5px 0;
background: #f0f0f0;
border: 1px solid #ddd;
cursor: grab;
}
</style>
- 关键点:
- 使用
ref定义响应式数据items和draggedIndex。 - 模板中,
v-for渲染列表项,draggable="true"启用拖拽。 - 事件监听:
dragstart、dragover和drop。
- 使用
步骤 3: 实现拖拽开始事件
在 setup 中添加 handleDragStart 方法,存储被拖拽项的索引:
<script>
import { ref } from 'vue';
export default {
setup() {
const items = ref([...]); // 同上
const draggedIndex = ref(null);
const handleDragStart = (index) => {
draggedIndex.value = index; // 存储当前拖拽项索引
event.dataTransfer.setData('text/plain', index); // 可选:传递数据
};
return { items, draggedIndex, handleDragStart };
}
};
</script>
- 解释:
handleDragStart在拖拽开始时触发,记录索引 $i$(其中 $i$ 是当前项位置)。这为后续排序提供基准。
步骤 4: 处理拖拽悬停事件
添加 handleDragOver 方法,允许放置并更新视觉反馈:
<script>
import { ref } from 'vue';
export default {
setup() {
// ... 同上
const handleDragOver = (event) => {
event.preventDefault(); // 必须阻止默认行为以允许 drop
event.dataTransfer.dropEffect = 'move'; // 设置拖拽效果为移动
};
return { ..., handleDragOver };
}
};
</script>
- 解释:
event.preventDefault()是关键,否则浏览器不会触发drop事件。dropEffect提供视觉提示(如光标变化)。
步骤 5: 实现放置事件和排序逻辑
在 setup 中添加 handleDrop 方法,更新列表顺序:
<script>
import { ref } from 'vue';
export default {
setup() {
// ... 同上
const handleDrop = (targetIndex) => {
event.preventDefault();
if (draggedIndex.value === null) return;
const sourceIndex = draggedIndex.value;
if (sourceIndex === targetIndex) return; // 相同位置不处理
// 更新列表:移动元素从索引 $i$ 到 $j$
const newItems = [...items.value];
const [movedItem] = newItems.splice(sourceIndex, 1); // 移除源项
newItems.splice(targetIndex, 0, movedItem); // 插入目标位置
items.value = newItems; // 更新响应式数据
draggedIndex.value = null; // 重置拖拽索引
};
return { ..., handleDrop };
}
};
</script>
- 排序逻辑:
- 当拖拽项从索引 $i$ 放置到索引 $j$ 时,列表顺序更新。
- 使用数组
splice方法:先移除源项,再插入目标位置。时间复杂度为 $O(n)$,适用于小型列表。 - 数学表示:如果原列表为 $[a_0, a_1, \dots, a_{i}, \dots, a_{n-1}]$,移动后变为 $[a_0, a_1, \dots, a_{j}, a_{i}, \dots]$(假设 $i < j$)。
步骤 6: 完整组件代码和优化
整合所有部分,并添加简单动画增强用户体验:
<template>
<ul class="list">
<li
v-for="(item, index) in items"
:key="item.id"
class="list-item"
draggable="true"
@dragstart="handleDragStart(index)"
@dragover="handleDragOver"
@drop="handleDrop(index)"
@dragend="handleDragEnd"
>
{{ item.name }}
</li>
</ul>
</template>
<script>
import { ref } from 'vue';
export default {
setup() {
const items = ref([
{ id: 1, name: '项目 A' },
{ id: 2, name: '项目 B' },
{ id: 3, name: '项目 C' }
]);
const draggedIndex = ref(null);
const handleDragStart = (index) => {
draggedIndex.value = index;
event.dataTransfer.effectAllowed = 'move';
};
const handleDragOver = (event) => {
event.preventDefault();
event.dataTransfer.dropEffect = 'move';
};
const handleDrop = (targetIndex) => {
event.preventDefault();
if (draggedIndex.value === null) return;
const sourceIndex = draggedIndex.value;
if (sourceIndex === targetIndex) return;
const newItems = [...items.value];
const [movedItem] = newItems.splice(sourceIndex, 1);
newItems.splice(targetIndex, 0, movedItem);
items.value = newItems;
};
const handleDragEnd = () => {
draggedIndex.value = null; // 确保重置
};
return { items, handleDragStart, handleDragOver, handleDrop, handleDragEnd };
}
};
</script>
<style scoped>
.list {
list-style: none;
padding: 0;
max-width: 300px;
}
.list-item {
padding: 12px;
margin: 8px 0;
background: #ffffff;
border: 1px solid #e0e0e0;
border-radius: 4px;
cursor: grab;
transition: all 0.3s ease; /* 添加过渡动画 */
}
.list-item:active {
cursor: grabbing;
}
.list-item.drag-over {
background: #e3f2fd; /* 悬停时高亮 */
}
</style>
总结
通过本教程,你已实现了一个支持拖拽排序的列表组件:
- Composition API 优势:逻辑封装在
setup中,易于复用(如抽离拖拽逻辑为独立函数)。 - 核心机制:利用 HTML5 拖拽事件和数组操作更新列表顺序。当拖拽项从索引 $i$ 移动到 $j$ 时,列表更新为 $[a_0, \dots, a_{j-1}, a_i, a_j, \dots]$(需处理边界)。
- 扩展建议:
- 添加动画库(如 Vue Transition)提升交互体验。
- 支持嵌套列表或跨组件拖拽。
- 结合 Vuex 或 Pinia 管理全局状态。
在实际项目中测试此组件,确保功能稳定。Composition API 使代码更模块化,便于维护和扩展。
更多推荐
所有评论(0)