Vue 3でドラッグ&ドロップを実装してみる
こんにちは、デザインチームの哲也です。
要素の並び替え機能を実装する機会があり、Vue 3でドラッグ&ドロップを実現する方法を調査しました。
いくつか方法がある中で、今回はVue 3との相性が良い vue-draggable-plus を使ってみました。
この記事では、vue-draggable-plusの導入から基本的な使い方までを紹介します。
vue-draggable-plusとは
vue-draggable-plusは、ドラッグ&ドロップライブラリであるSortableJSをVue 3で扱いやすくしたライブラリです。
refとv-modelをそのまま利用できるため、Vue 3の書き方に沿って手軽に実装できます。また、ドラッグ&ドロップに必要な複雑な処理を自分で実装することなく、少ないコードで並び替え機能を実現できるのも特徴です。
基本的な使い方以外にも、handleやdisabledなどのオプション、イベント、カスタムディレクティブなどさまざまな機能が用意されています。
興味がある方は、公式ドキュメントもあわせて確認してみてください。
インストール
はじめにライブラリをインストールします。
npm install vue-draggable-plus
※本記事は Vue 3.5.x、vue-draggable-plus 0.6.1 の環境で動作確認しています。
リスト内で並び替える
まずは、1つのリスト内で要素を並び替えるサンプルを作成します。
今回はVueDraggableコンポーネントを利用しています。
vue-draggable-plusではカスタムディレクティブ(v-draggable)を利用する方法もありますが、今回は構造が分かりやすいコンポーネント形式で紹介します。
サンプルコード
<template>
<div class="box">
<VueDraggable v-model="items" class="list">
<div
v-for="item in items"
:key="item.id"
class="item"
>
{{ item.name }}
</div>
</VueDraggable>
</div>
<pre>{{ JSON.stringify(items, null, 2) }}</pre>
</template>
<script setup>
import { ref } from 'vue'
import { VueDraggable } from 'vue-draggable-plus'
const items = ref([
{ id: 1, name: 'アイテムA' },
{ id: 2, name: 'アイテムB' },
{ id: 3, name: 'アイテムC' },
])
</script>
<style scoped>
.box {
width: 240px;
padding: 16px;
margin-bottom: 20px;
border: 1px solid #ddd;
background: #f7f7f7;
}
.list {
min-height: 120px;
}
.item {
padding: 12px;
margin-bottom: 8px;
border: 1px solid #ccc;
background: #fff;
cursor: move;
}
pre {
padding: 16px;
background: #f7f7f7;
}
</style>
これだけで、リストをドラッグ&ドロップで並び替えられるようになります。
実際の動きは次のようになります。

animationオプション
ドラッグ時の動きを滑らかにしたい場合は、animationを指定します。
<VueDraggable
v-model="items"
:animation="150"
class="list"
>
animationはドラッグ時のアニメーション時間(ミリ秒)を指定するオプションです。
例えば 150 を指定すると、要素の移動が滑らかにアニメーション表示されます。
実際の動きは次のようになります。

ボックス間で要素を移動する
次に、2つのボックスを用意し、ボックス間で要素を移動できるサンプルを作成します。
サンプルコード
<template>
<div class="boxes">
<div class="box">
<h3>ボックスA</h3>
<VueDraggable
v-model="boxAItems"
group="items"
:animation="150"
class="list"
>
<div
v-for="item in boxAItems"
:key="item.id"
class="item"
>
{{ item.name }}
</div>
</VueDraggable>
</div>
<div class="box">
<h3>ボックスB</h3>
<VueDraggable
v-model="boxBItems"
group="items"
:animation="150"
class="list"
>
<div
v-for="item in boxBItems"
:key="item.id"
class="item"
>
{{ item.name }}
</div>
</VueDraggable>
</div>
</div>
<div class="data-view">
<div>
<h3>boxAItems</h3>
<pre>{{ JSON.stringify(boxAItems, null, 2) }}</pre>
</div>
<div>
<h3>boxBItems</h3>
<pre>{{ JSON.stringify(boxBItems, null, 2) }}</pre>
</div>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { VueDraggable } from 'vue-draggable-plus'
const boxAItems = ref([
{ id: 1, name: 'アイテムA' },
{ id: 2, name: 'アイテムB' },
{ id: 3, name: 'アイテムC' },
])
const boxBItems = ref([
{ id: 4, name: 'アイテムD' },
{ id: 5, name: 'アイテムE' },
])
</script>
<style scoped>
.boxes,
.data-view {
display: grid;
grid-template-columns: 272px 272px;
gap: 24px;
}
.box {
width: 240px;
padding: 16px;
margin-bottom: 20px;
border: 1px solid #ddd;
background: #f7f7f7;
}
.list {
min-height: 160px;
}
.item {
padding: 12px;
margin-bottom: 8px;
border: 1px solid #ccc;
background: #fff;
cursor: move;
}
pre {
padding: 16px;
background: #f7f7f7;
}
</style>
同じgroup名(group="items")を指定すると、ボックスAとボックスBの間で要素を移動できます。
画面上の表示だけでなく、下に表示しているboxAItemsとboxBItemsの中身も移動後の状態に更新されます。
実際の動きは次のようになります。

v-modelとkeyのポイント
vue-draggable-plusでは、v-modelに配列を渡すことで、ドラッグ&ドロップ後の並び順が自動で同期されます。
そのため、ドラッグ終了後に自分で配列を書き換える必要はありません。
また、v-forで表示する要素には、一意のkeyを指定します。
:key="item.id"
インデックスではなくIDなどの一意な値を指定することで、ドラッグ後もVueが要素を正しく管理できます。
実際に使ってみた感想
導入自体はとても簡単で、基本的なドラッグ&ドロップであれば少ないコードで実装できました。
v-modelでデータが同期されるため、並び替え後のAPI連携も実装しやすい印象です。
ただ、実際の業務では並び順をどのタイミングで保存するか、ドラッグできる条件を制御するなど考慮する点はあるなと思いました。
まずは基本的な使い方を理解しておくと、実際の開発でも応用しやすいと感じました。
まとめ
今回はvue-draggable-plusを使って、Vue 3でドラッグ&ドロップを実装する方法を紹介しました。
少ないコードでドラッグ&ドロップを実装でき、v-modelによるデータ同期も自動で行われるため、非常に扱いやすいライブラリだと感じました。
ドラッグ&ドロップ機能を実装する際の参考になれば幸いです。
ラクーンホールディングスではエンジニア・デザイナーを大募集中です!
少しでも興味がありましたらぜひご応募ください!






