はじめに
Vue 3でComposition APIを使っていると、「この処理、毎回書くの面倒だな」と思うことがありませんか。マウス座標の取得、ローカルストレージの管理、ウィンドウサイズの監視...。こういった定型処理を自分で書くのは、正直なところ時間の無駄なんですよね。
そこで今回紹介するのがVueUseです。Vue 3向けのコンポジション関数をまとめたライブラリで、GitHubスター数22,000以上、月間ダウンロード数1,700万という圧倒的な支持を得ています。個人的には、Vue開発するなら入れておいて損はないライブラリだと思います。
説明を読むより、実際に動かした方が早いと思います。VueUseのComposable関数がどれくらい手軽か、先に触って確かめたい方はこちらからどうぞ。
VueUseの特徴・メリット
200以上の関数が使い放題
VueUseには200以上のコンポジション関数が用意されています。カテゴリーも豊富で、State、Browser、Sensors、Network、Animation、Timeなど、開発で必要になる処理がほぼ網羅されています。
ツリーシェイク対応でバンドルサイズも安心
「200以上の関数って、バンドルサイズ大丈夫?」という心配は無用です。VueUseは完全にツリーシェイク対応なので、実際に使った関数だけがバンドルに含まれます。パフォーマンス重視の案件でも安心して導入できますね。
TypeScript完全対応
TypeScriptで完全に記述されており、型定義も充実しています。エディタの補完がしっかり効くので、開発効率が上がるのはもちろん、実行時エラーの防止にもつながります。
SSRフレンドリー
Nuxt 3などのSSR環境でも問題なく動作します。サーバーサイドでwindowやdocumentにアクセスするとエラーになる、というお決まりのハマりポイントもVueUseなら適切に処理してくれます。
アドオンで拡張可能
Router、Firebase、RxJSなど、主要なライブラリとの連携用アドオンも提供されています。既存のプロジェクトにも組み込みやすい設計になっています。
インストール方法
インストールは非常にシンプルです。
# npm
npm install @vueuse/core
# yarn
yarn add @vueuse/core
# pnpm
pnpm add @vueuse/core
注意点: バージョン14.0以降はVue 3.5以上が必須になっています。古いVueを使っている場合は、先にアップグレードが必要です。
VueUseのサンプルを動かす
まずはuseMouseという1つのComposable関数だけで、何が実現できるかを見てみましょう。マウス座標をリアクティブに追跡するサンプルです。下の枠内でマウスを動かすと、x/y座標がその場で更新されます。
要点だけを抜き出すと、次のようなコードになります。
<script setup lang="ts">
import { useMouse } from '@vueuse/core'
// x, y はマウス座標を追跡するリアクティブな値
// sourceType はマウス操作かタッチ操作かを表す
const { x, y, sourceType } = useMouse()
</script>
<template>
<p>x: {{ x }} / y: {{ y }} / {{ sourceType }}</p>
</template>
実際に動かせるのが下のサンプルです。枠内でマウスを動かして、座標の変化を確かめてみてください。
useMouse()は内部でmousemoveイベントのリスナー登録・解除を自動的に行ってくれるため、onMountedやonUnmountedを自分で書く必要がありません。タッチ操作かどうかを判定したい場合は、戻り値のsourceTypeを見るだけで済みます。VueUseのComposable関数はこのように「値を返すだけ」で終わらず、後片付けまで面倒を見てくれるのが特徴です。
基本的な使い方
VueUseの使い方は非常に直感的です。必要な関数をインポートして、setup内で呼び出すだけ。
useMouse - マウス座標の取得
<script setup lang="ts">
import { useMouse } from '@vueuse/core'
const { x, y } = useMouse()
</script>
<template>
<div>
マウス座標: {{ x }}, {{ y }}
</div>
</template>
これだけでマウス座標がリアクティブに取得できます。自前で実装するとaddEventListenerとremoveEventListenerの管理が面倒ですが、VueUseなら一行で完結します。
useLocalStorage - ローカルストレージの永続化
<script setup lang="ts">
import { useLocalStorage } from '@vueuse/core'
// 'settings'というキーで保存、デフォルト値はオブジェクト
const settings = useLocalStorage('settings', {
theme: 'dark',
language: 'ja'
})
// 値を変更すると自動的にローカルストレージに保存される
settings.value.theme = 'light'
</script>
ローカルストレージへの読み書きが、普通のrefと同じ感覚で扱えます。これ、意外と便利なんですよね。
useWindowSize - ウィンドウサイズの監視
<script setup lang="ts">
import { useWindowSize } from '@vueuse/core'
const { width, height } = useWindowSize()
</script>
<template>
<div>
ウィンドウサイズ: {{ width }} x {{ height }}
</div>
</template>
リサイズイベントのリスナー管理を気にせず、ウィンドウサイズをリアクティブに取得できます。
実践的なユースケース
状態を永続化する - useDark・useToggleでダークモード切り替え
テーマのようなユーザー設定は、リロードしても消えてほしくない典型的な「状態」です。VueUseにはuseLocalStorageのような状態系Composable関数が複数用意されていて、useDarkもその一つ。ストレージへの読み書きを意識せず、状態を扱えるようにしてくれます。
<script setup lang="ts">
import { useDark, useToggle } from '@vueuse/core'
const isDark = useDark()
const toggleDark = useToggle(isDark)
</script>
<template>
<button @click="toggleDark()">
{{ isDark ? 'ライトモードに切り替え' : 'ダークモードに切り替え' }}
</button>
</template>
useDarkはシステムの設定を検知しつつ、ユーザーの選択をローカルストレージに保存してくれます。ダークモード実装がこの数行で完結するのは、かなり時短になります。実際に動かして、ボタンを押すとテーマの状態がどう反応するか見てみましょう。
useDark()が返すisDarkはローカルストレージ(既定ではvueuse-color-schemeというキー)と同期しているため、ボタンを押して切り替えた状態はページを開き直しても保持されます。保存先のキーを変えたい場合はuseDark({ storageKey: 'my-theme' })のようにオプションを渡します。
スクロールをセンシングする - useInfiniteScrollで無限リスト
一覧画面を下までスクロールしたときに次のページを読み込む「無限スクロール」は、スクロール位置を継続的に監視するセンサー系の実装が必要になります。VueUseのuseInfiniteScrollは、useMouseやuseElementSizeと同じセンサー系のComposable関数で、この監視処理をまとめて提供してくれます。
<script setup lang="ts">
import { useInfiniteScroll } from '@vueuse/core'
import { ref } from 'vue'
const el = ref<HTMLElement | null>(null)
const items = ref<string[]>([])
useInfiniteScroll(
el,
async () => {
// 末尾に到達したら追加データを取得
const newItems = await fetchMoreItems()
items.value.push(...newItems)
},
{ distance: 100 } // 末尾から100px手前で発火
)
</script>
<template>
<div ref="el" style="height: 400px; overflow-y: auto;">
<div v-for="item in items" :key="item">{{ item }}</div>
</div>
</template>
無限スクロールの実装も、Intersection Observerの設定を自前で書く必要がありません。実際に動かして、スクロールするたびにリストが伸びていく様子を確認してみましょう。以下のサンプルでは、API通信の代わりにダミーデータを生成しています。
distanceオプションを大きくするほど、リストの末尾に近づく前の早いタイミングで追加読み込みが発火します。実際のアプリでは、このコールバックの中身をAPIのfetch呼び出しに差し替えるだけで、本物の無限スクロールになります。
実行頻度を制御する - useDebounceFn・useThrottleFnで検索を最適化
入力のたびに検索APIを叩いたり、スクロールのたびに重い処理を実行したりすると、パフォーマンスが悪化します。VueUseのuseDebounceFnとuseThrottleFnは、こうした「実行頻度を間引きたい」というユーティリティ系のニーズに応える関数です。
<script setup lang="ts">
import { useDebounceFn, useThrottleFn } from '@vueuse/core'
// 300ms間入力がなければ検索実行
const debouncedSearch = useDebounceFn((query: string) => {
console.log('検索:', query)
}, 300)
// 最大1秒に1回だけ実行
const throttledScroll = useThrottleFn(() => {
console.log('スクロール処理')
}, 1000)
</script>
検索フォームの入力や、スクロールイベントの最適化でよく使うパターンですね。実際に入力して、通常の実行回数とuseDebounceFn適用後の実行回数がどれだけ違うか比べてみましょう。
入力イベント回数(rawCount)はキーを押すたびに増えますが、実際に検索が実行された回数(debouncedCount)は300ms入力が止まったときだけ増えます。useDebounceFnの第2引数のミリ秒数を1000のように増やすと、より長く入力が止まるまで実行されなくなる違いも試してみてください。
まとめ
VueUseは、Vue 3開発における「あったら便利」な関数を大量に提供してくれるライブラリです。個人的には以下の点が特に気に入っています。
- 学習コストが低い: Composition APIを理解していれば、すぐに使い始められる
- コード量の削減: 定型処理を書く手間が省ける
- 品質の担保: 十分にテストされたコードを使える安心感
30代になって思うのは、「車輪の再発明」に時間を使うのは本当にもったいないということ。VueUseのような良質なライブラリを活用して、本質的な実装に集中したほうがQOL上がりますよね。
Vue 3を使っているなら、導入しない理由はないと思います。まずは公式ドキュメントで関数一覧を眺めてみてください。「これ、いつも自分で書いてた!」という関数がきっと見つかるはずです。