Vue.jsで開発していると、「マウス位置を取得したい」「ローカルストレージに保存したい」「デバウンス処理を実装したい」といった、よくある機能を毎回一から実装するのは面倒ですよね。
そんな時に役立つのがVueUseです。
VueUseは、Vue.jsのComposition API向けに作られた、200以上の実用的なコンポーザブル関数のコレクションです。ブラウザAPI、ユーティリティ関数、状態管理など、日常的な開発でよく使う機能を簡単に利用できるようにしてくれます。
今回は、VueUseの公式ドキュメントを参考にしながら、概要から実践的な使い方まで紹介していきます。
VueUseとは
VueUseは、Vue 3のComposition APIを活用した、再利用可能なコンポーザブル関数のライブラリです。
ReactのHooksライブラリにインスパイアされており、Vue.js開発者がよく使う機能を簡単に実装できるように設計されています。
公式ドキュメントを見ると、従来は何十行も書いていたコードが数行で済むようになるようです。
特に、ブラウザAPIを扱う際のイベントリスナーの管理や、ローカルストレージとの連携などが非常に簡単になるとのことです。
VueUseの特徴として、200以上のコンポーザブルが用意されており、ブラウザAPI、ユーティリティ、状態管理など幅広い機能を提供しています。TypeScriptの完全サポートもあり、型安全な開発が可能です。
また、Vue 2、Vue 3、Nuxtなど様々な環境で使用でき、ツリーシェイキングにも対応しているため、必要な機能のみをインポートしてバンドルサイズを最適化できます。
さらに、多くのコンポーザブルがアクセシビリティを考慮して設計されており、SSR環境でも動作するなど、実用的な機能が満載です。
インストール
VueUseのインストールは非常にシンプルです。npmまたはyarnでインストールするだけです。
npm install @vueuse/core
# または
yarn add @vueuse/core
Vue 2を使用している場合は、追加で@vue/composition-apiが必要になります。これは、Vue 2でComposition APIを使うために必要なパッケージです。
npm install @vueuse/core @vue/composition-api
基本的な使い方
VueUseのコンポーザブルは、通常の関数のようにインポートして使うだけです。
例えば、マウス位置を取得したい場合はuseMouseを、カウンター機能が欲しい場合はuseCounterをインポートするだけです。
<template>
<div>
<p>マウス位置: x={{ x }}, y={{ y }}</p>
<p>クリック回数: {{ count }}</p>
<button @click="increment">クリック</button>
</div>
</template>
<script setup>
import { useMouse, useCounter } from '@vueuse/core'
// マウス位置を追跡
const { x, y } = useMouse()
// カウンター
const { count, increment } = useCounter()
</script>
主要なカテゴリと機能
VueUseのコンポーザブルは、機能ごとにカテゴリ分けされています。
ここでは、実際の開発でよく使う主要なカテゴリを紹介していきます。
それぞれのカテゴリで、ドキュメントでよく紹介されている機能を中心に説明します。
1. State(状態管理)
まずは状態管理に関するコンポーザブルから見ていきましょう。
これらは、アプリケーションの状態を管理する際に非常に便利です。
useCounter
カウンター機能が必要になった時、従来はrefを使って自分で実装する必要がありましたが、VueUseのuseCounterを使えば、わずか1行で実装できるようです。
<template>
<div>
<p>カウント: {{ count }}</p>
<button @click="inc()">+1</button>
<button @click="dec()">-1</button>
<button @click="set(100)">100に設定</button>
<button @click="reset()">リセット</button>
</div>
</template>
<script setup>
import { useCounter } from '@vueuse/core'
const { count, inc, dec, set, reset } = useCounter(0)
</script>
useToggle
モーダルの開閉やメニューの表示/非表示など、真偽値の切り替えが必要な場面は多いですよね。
useToggleを使えば、このような切り替え処理を簡単に実装できます。
<template>
<div>
<p>状態: {{ isOpen ? '開く' : '閉じる' }}</p>
<button @click="toggle()">切り替え</button>
<button @click="setTrue()">開く</button>
<button @click="setFalse()">閉じる</button>
</div>
</template>
<script setup>
import { useToggle } from '@vueuse/core'
const [isOpen, toggle, setTrue, setFalse] = useToggle(false)
</script>
useLocalStorage / useSessionStorage
ユーザーの設定を保存したい時、従来はlocalStorage.setItemやlocalStorage.getItemを毎回書く必要がありましたが、useLocalStorageを使えば、通常のrefと同じように扱えるようです。
しかも、値が変更されると自動的にローカルストレージに保存されるので、非常に便利です。
<template>
<div>
<input v-model="name" placeholder="名前を入力" />
<p>保存された名前: {{ storedName }}</p>
</div>
</template>
<script setup>
import { useLocalStorage } from '@vueuse/core'
// ローカルストレージに自動的に保存される
const name = useLocalStorage('name', 'デフォルト名')
// 別のコンポーネントでも同じキーでアクセス可能
const storedName = useLocalStorage('name', 'デフォルト名')
</script>
2. Browser(ブラウザAPI)
ブラウザのAPIを扱う際、イベントリスナーの追加や削除を手動で管理するのは面倒です。
VueUseのブラウザAPI関連のコンポーザブルを使えば、これらの処理を自動で行ってくれます。
useMouse
マウス位置を取得したい時、従来はmousemoveイベントのリスナーを追加して、コンポーネントのアンマウント時に削除する必要がありました。
useMouseを使えば、この処理を自動で行ってくれるようです。
<template>
<div>
<p>マウス位置: ({{ x }}, {{ y }})</p>
</div>
</template>
<script setup>
import { useMouse } from '@vueuse/core'
const { x, y } = useMouse()
</script>
useWindowSize
レスポンシブデザインを実装する際、ウィンドウサイズを監視する必要があります。
useWindowSizeを使えば、ウィンドウサイズが変更されるたびに自動的に値が更新されます。
<template>
<div>
<p>ウィンドウサイズ: {{ width }} x {{ height }}</p>
</div>
</template>
<script setup>
import { useWindowSize } from '@vueuse/core'
const { width, height } = useWindowSize()
</script>
useScroll
スクロール位置に応じてUIを変更したい時、useScrollが便利です。
スクロール位置だけでなく、スクロール中かどうかも判定できるので、スクロールバーの表示/非表示などに活用できます。
<template>
<div ref="element" style="height: 2000px;">
<p>スクロール位置: x={{ x }}, y={{ y }}</p>
<p>スクロール可能: {{ isScrolling ? 'はい' : 'いいえ' }}</p>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { useScroll } from '@vueuse/core'
const element = ref()
const { x, y, isScrolling } = useScroll(element)
</script>
useClipboard
「このテキストをコピー」ボタンを実装する際、従来はnavigator.clipboard.writeTextを使う必要がありましたが、エラーハンドリングやコピー成功の判定を自分で実装する必要がありました。
useClipboardを使えば、これらの処理が簡単に実装できるようです。
<template>
<div>
<input v-model="text" placeholder="コピーするテキスト" />
<button @click="copy(text)">コピー</button>
<p v-if="copied">コピーしました!</p>
<p>クリップボード: {{ clipboardText }}</p>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { useClipboard } from '@vueuse/core'
const text = ref('')
const { copy, copied, text: clipboardText } = useClipboard()
</script>
useGeolocation
位置情報を取得する際、ブラウザのサポート状況を確認したり、エラーハンドリングを行ったりする必要があります。
useGeolocationを使えば、これらの処理を簡単に実装できます。
<template>
<div>
<button @click="getCurrentPosition()">位置情報を取得</button>
<p v-if="isSupported">
緯度: {{ coords.latitude }}<br>
経度: {{ coords.longitude }}
</p>
<p v-else>位置情報はサポートされていません</p>
</div>
</template>
<script setup>
import { useGeolocation } from '@vueuse/core'
const { coords, isSupported, getCurrentPosition } = useGeolocation()
</script>
3. Sensors(センサー)
モバイルデバイスのセンサー情報を取得したい時、VueUseのセンサー関連のコンポーザブルが便利です。
スマートフォンやタブレットの加速度センサーやジャイロスコープの情報を簡単に取得できます。
useDeviceMotion
スマートフォンを振ったり傾けたりした時のモーション情報を取得したい時、useDeviceMotionが役立ちます。加速度や回転速度などの情報をリアルタイムで取得できます。
<template>
<div>
<p>加速度: x={{ acceleration.x }}, y={{ acceleration.y }}, z={{ acceleration.z }}</p>
<p>回転速度: x={{ rotationRate.alpha }}, y={{ rotationRate.beta }}, z={{ rotationRate.gamma }}</p>
</div>
</template>
<script setup>
import { useDeviceMotion } from '@vueuse/core'
const { acceleration, rotationRate } = useDeviceMotion()
</script>
useDeviceOrientation
デバイスの向き(縦向き/横向き)を監視したい時、useDeviceOrientationが便利です。
画面の向きに応じてUIを変更する際に活用できます。
<template>
<div>
<p>アルファ: {{ alpha }}</p>
<p>ベータ: {{ beta }}</p>
<p>ガンマ: {{ gamma }}</p>
</div>
</template>
<script setup>
import { useDeviceOrientation } from '@vueuse/core'
const { alpha, beta, gamma } = useDeviceOrientation()
</script>
4. Network(ネットワーク)
ネットワークの状態を監視したい時、VueUseのネットワーク関連のコンポーザブルが便利です。
オフライン時の処理や、ネットワーク速度に応じた最適化などに活用できます。
useOnline
ユーザーがオフラインになった時に、適切なメッセージを表示したい時がありますよね。
useOnlineを使えば、オンライン/オフライン状態を簡単に監視できます。
<template>
<div>
<p v-if="isOnline">オンライン</p>
<p v-else>オフライン</p>
</div>
</template>
<script setup>
import { useOnline } from '@vueuse/core'
const isOnline = useOnline()
</script>
useNetwork
ネットワークの接続タイプや速度などの詳細な情報が必要な時、useNetworkが便利です。
低速回線のユーザーに対して、画像の読み込みを遅らせるなどの最適化に活用できます。
<template>
<div>
<p>オンライン: {{ isOnline ? 'はい' : 'いいえ' }}</p>
<p>接続タイプ: {{ effectiveType }}</p>
<p>ダウンリンク: {{ downlink }} Mbps</p>
<p>RTT: {{ rtt }} ms</p>
</div>
</template>
<script setup>
import { useNetwork } from '@vueuse/core'
const { isOnline, effectiveType, downlink, rtt } = useNetwork()
</script>
5. Animation(アニメーション)
値の変化をスムーズにアニメーション化したい時、VueUseのアニメーション関連のコンポーザブルが便利です。CSSアニメーションを使わずに、JavaScriptでアニメーションを実装できます。
useTransition
数値の変化をスムーズにアニメーション化したい時、useTransitionが役立ちます。
プログレスバーやカウントアップアニメーションなどに活用できます。
<template>
<div>
<button @click="target = target === 0 ? 100 : 0">切り替え</button>
<div :style="{ width: `${output}px`, height: '50px', background: 'blue' }"></div>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { useTransition, TransitionPresets } from '@vueuse/core'
const target = ref(0)
const output = useTransition(target, {
duration: 1000,
transition: TransitionPresets.easeInOutCubic
})
</script>
6. Utilities(ユーティリティ)
開発でよく使うユーティリティ関数も、VueUseで簡単に実装できます。
特に、デバウンスやスロットル処理は、検索機能やスクロールイベントの処理などで頻繁に使います。
useDebounce / useThrottle
検索フォームで、ユーザーが入力するたびにAPIを呼び出すのは非効率ですよね。
useDebounceを使えば、ユーザーが入力し終わってから一定時間後に処理を実行できます。
一方、useThrottleは、一定時間ごとに処理を実行したい時に便利です。
<template>
<div>
<input v-model="input" placeholder="入力してください" />
<p>デバウンスされた値: {{ debouncedInput }}</p>
<p>スロットルされた値: {{ throttledInput }}</p>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { useDebounce, useThrottle } from '@vueuse/core'
const input = ref('')
const debouncedInput = useDebounce(input, 500)
const throttledInput = useThrottle(input, 500)
</script>
useInterval
定期的に処理を実行したい時、setIntervalを使いますが、コンポーネントのアンマウント時にクリーンアップする必要があります。
useIntervalを使えば、この処理を自動で行ってくれます。
さらに、一時停止や再開も簡単にできます。
<template>
<div>
<p>カウント: {{ count }}</p>
<button @click="pause()">一時停止</button>
<button @click="resume()">再開</button>
<button @click="stop()">停止</button>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { useInterval } from '@vueuse/core'
const count = ref(0)
const { pause, resume, stop } = useInterval(() => {
count.value++
}, 1000)
</script>
useTimeout
一定時間後に処理を実行したい時、setTimeoutを使いますが、コンポーネントのアンマウント前にキャンセルする必要があります。
useTimeoutを使えば、この処理を簡単に管理できます。
<template>
<div>
<button @click="start()">3秒後にアラート</button>
<button @click="stop()">キャンセル</button>
</div>
</template>
<script setup>
import { useTimeout } from '@vueuse/core'
const { start, stop } = useTimeout(() => {
alert('3秒経過しました!')
}, 3000)
</script>
useAsyncState
APIからデータを取得する際、ローディング状態やエラー状態を管理する必要があります。
useAsyncStateを使えば、これらの状態を簡単に管理できるようです。
従来は、refでloadingやerrorを管理する必要がありましたが、これらが1つのコンポーザブルで完結するので非常に便利です。
<template>
<div>
<button @click="execute()">データを取得</button>
<p v-if="isLoading">読み込み中...</p>
<p v-else-if="error">エラー: {{ error }}</p>
<p v-else>データ: {{ state }}</p>
</div>
</template>
<script setup>
import { useAsyncState } from '@vueuse/core'
const fetchData = async () => {
const response = await fetch('https://api.example.com/data')
return response.json()
}
const { state, isLoading, error, execute } = useAsyncState(fetchData, null, {
immediate: false
})
</script>
7. Component(コンポーネント)
コンポーネントの要素に関する情報を取得したい時、VueUseのコンポーネント関連のコンポーザブルが便利です。要素が画面に表示されているかどうかや、ビューポートに入ったかどうかを簡単に判定できます。
useElementVisibility
要素が画面に表示されているかどうかを判定したい時、useElementVisibilityが役立ちます。
スクロールに応じて要素を表示/非表示にする際に活用できます。
<template>
<div>
<div ref="target" style="height: 1000px; margin-top: 500px;">
この要素が表示されているか: {{ isVisible ? 'はい' : 'いいえ' }}
</div>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { useElementVisibility } from '@vueuse/core'
const target = ref()
const isVisible = useElementVisibility(target)
</script>
useIntersectionObserver
要素がビューポートに入った時に処理を実行したい時、Intersection Observer APIを使いますが、設定が少し複雑です。
useIntersectionObserverを使えば、この処理を簡単に実装できます。
無限スクロールや、スクロールに応じたアニメーションなどに活用できます。
<template>
<div>
<div ref="target" style="height: 1000px; margin-top: 500px;">
要素がビューポートに入りました: {{ isIntersecting ? 'はい' : 'いいえ' }}
</div>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { useIntersectionObserver } from '@vueuse/core'
const target = ref()
const { isIntersecting } = useIntersectionObserver(target)
</script>
8. Watch(監視)
Vueのwatchを使う際、デバウンスやスロットルを適用したい時があります。
VueUseの監視関連のコンポーザブルを使えば、これらの処理を簡単に実装できます。
useDebouncedWatch / useThrottledWatch
検索フォームで、ユーザーが入力するたびに検索を実行するのではなく、入力が終わってから検索を実行したい時、useDebouncedWatchが便利です。
useThrottledWatchは、一定時間ごとに処理を実行したい時に使います。
<template>
<div>
<input v-model="searchQuery" placeholder="検索" />
<p>検索結果: {{ searchResults }}</p>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { useDebouncedWatch } from '@vueuse/core'
const searchQuery = ref('')
const searchResults = ref('')
useDebouncedWatch(searchQuery, (value) => {
// 500ms後に検索を実行
searchResults.value = `「${value}」の検索結果`
}, { debounce: 500 })
</script>
実践的な使用例
ここまで、VueUseの主要な機能を紹介してきましたが、実際の開発でどのように使うのか、具体的な例を見ていきましょう。
公式ドキュメントに記載されている実践的な例を紹介します。
例1: ダークモードの実装
ダークモードを実装する際、従来はlocalStorageに保存して、watchで監視する必要がありましたが、useDarkとuseToggleを使えば、わずか数行で実装できるようです。
<template>
<div :class="{ dark: isDark }">
<button @click="toggle()">テーマを切り替え</button>
<p>現在のテーマ: {{ isDark ? 'ダーク' : 'ライト' }}</p>
</div>
</template>
<script setup>
import { useDark, useToggle } from '@vueuse/core'
const isDark = useDark()
const toggle = useToggle(isDark)
</script>
<style>
.dark {
background-color: #1a1a1a;
color: #fff;
}
</style>
例2: 無限スクロール
SNSアプリなどでよく見る無限スクロール機能も、VueUseを使えば簡単に実装できます。
useInfiniteScrollを使えば、要素がビューポートに入った時に自動的にデータを読み込めます。
<template>
<div>
<div v-for="item in items" :key="item.id">
{{ item.content }}
</div>
<div ref="target"></div>
<p v-if="isLoading">読み込み中...</p>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import { useIntersectionObserver, useInfiniteScroll } from '@vueuse/core'
const items = ref([])
const target = ref()
const isLoading = ref(false)
const loadMore = async () => {
isLoading.value = true
// APIからデータを取得
const newItems = await fetchItems(items.value.length)
items.value.push(...newItems)
isLoading.value = false
}
useInfiniteScroll(target, loadMore, {
distance: 10
})
</script>
例3: フォームのバリデーション
フォームのバリデーションを実装する際、ユーザーが入力するたびにバリデーションを実行すると、パフォーマンスに影響が出る可能性があります。
useDebounceを使って、入力が終わってからバリデーションを実行することで、パフォーマンスを改善できます。
<template>
<form @submit.prevent="handleSubmit">
<input v-model="email" type="email" />
<p v-if="!isEmailValid">有効なメールアドレスを入力してください</p>
<input v-model="password" type="password" />
<p v-if="!isPasswordValid">パスワードは8文字以上です</p>
<button type="submit" :disabled="!isFormValid">送信</button>
</form>
</template>
<script setup>
import { ref, computed } from 'vue'
import { useDebounce } from '@vueuse/core'
const email = ref('')
const password = ref('')
const debouncedEmail = useDebounce(email, 300)
const debouncedPassword = useDebounce(password, 300)
const isEmailValid = computed(() => {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(debouncedEmail.value)
})
const isPasswordValid = computed(() => {
return debouncedPassword.value.length >= 8
})
const isFormValid = computed(() => {
return isEmailValid.value && isPasswordValid.value
})
const handleSubmit = () => {
if (isFormValid.value) {
// フォーム送信処理
console.log('送信:', { email: email.value, password: password.value })
}
}
</script>
例4: ドラッグ&ドロップ
要素をドラッグして移動させたい時、従来はmousedown、mousemove、mouseupイベントを手動で管理する必要がありましたが、useDraggableを使えば、この処理を自動で行ってくれるようです。
<template>
<div
ref="target"
:style="{ position: 'absolute', left: `${x}px`, top: `${y}px`, cursor: 'move' }"
>
ドラッグして移動
</div>
</template>
<script setup>
import { ref } from 'vue'
import { useDraggable } from '@vueuse/core'
const target = ref()
const { x, y } = useDraggable(target)
</script>
カスタムコンポーザブルの作成
VueUseのコンポーザブルを参考に、独自のコンポーザブルを作成することもできます。
例えば、APIからデータを取得する処理を、VueUseのuseFetchを使ってラップすることで、プロジェクト全体で統一されたAPI呼び出しを実装できます。
// composables/useApi.js
import { ref } from 'vue'
import { useFetch } from '@vueuse/core'
export function useApi(url) {
const { data, error, isFetching, execute } = useFetch(url, {
immediate: false
}).json()
return {
data,
error,
isLoading: isFetching,
refetch: execute
}
}
パフォーマンスの最適化
VueUseは、ツリーシェイキングに対応しているため、必要な機能のみをインポートすることで、バンドルサイズを最適化できます。
例えば、useMouseとuseCounterだけを使いたい場合は、以下のようにインポートします。
// 必要な機能のみをインポート
import { useMouse, useCounter } from '@vueuse/core'
また、多くのコンポーザブルは、オプションでパフォーマンスを調整できます。例えば、useScrollを使う際、スクロールイベントが頻繁に発火するとパフォーマンスに影響が出る可能性があるため、throttleオプションで更新頻度を調整できます。
import { useScroll } from '@vueuse/core'
const { x, y } = useScroll(element, {
throttle: 100 // 100msごとに更新
})
TypeScriptとの統合
VueUseは、TypeScriptを完全にサポートしています。
型定義が自動的に提供されるため、型安全な開発が可能です。
例えば、useCounterを使う際、countはRef<number>型、incやdecは() => void型として推論されます。
import { useCounter } from '@vueuse/core'
const { count, inc, dec } = useCounter(0)
// countはRef<number>型
// inc, decは() => void型
これにより、IDEの補完が効きやすくなり、開発効率が向上します。
まとめ
VueUseは、Vue.js開発を大幅に効率化する強力なライブラリです。
200以上のコンポーザブル関数により、日常的な開発でよく使う機能を簡単に実装できます。
公式ドキュメントを見ると、従来は何十行も書いていたコードが数行で済むようになり、開発効率が格段に上がるようです。
特に、ブラウザAPIを扱う際のイベントリスナーの管理や、ローカルストレージとの連携、デバウンス処理などが非常に簡単になるとのことです。
また、VueUseのコンポーザブルは、適切にライフサイクル管理が行われているため、メモリリークの心配もないようです。
さらに、TypeScriptの完全サポートにより、型安全な開発が可能です。
まずは、useCounterやuseMouseなどの基本的なコンポーザブルから始めて、徐々に複雑な機能を実装していくことをお勧めします。
VueUseを使うことで、コードの記述量を減らし、保守性を向上させ、開発速度を大幅に向上させることができるようです。
もし、VueUseで実装したい機能が見つからない場合は、VueUseのコンポーザブルを参考に、独自のコンポーザブルを作成することもできます。公式ドキュメントによると、VueUseのソースコードは非常に読みやすく、学習にも最適とのことです。