こんにちは、かつコーチです。
前回は、axiosを使ってAPIからデータを取得・表示する方法と、その処理をComposableに切り出すところまで解説しました。
今回は、「よく使うComposableを毎回自作しなくても、すでに用意されているライブラリがある」という話をします。
その代表格がVueUseです。
VueUseとは?
200以上のComposableが揃ったユーティリティ集
VueUseは、Vue 3のrefやcomputed、Composition APIを前提に作られた、Composable関数のコレクションです。
「マウス座標を取得したい」「ウィンドウサイズを監視したい」「ローカルストレージと連携したい」といった、実務でよく出てくる処理があらかじめ関数として用意されています。
なぜ自作より先にVueUseを検討すべきか
自分でComposableを書くこと自体は悪くありませんが、次のような処理は特に「車輪の再発明」になりがちです。
- ブラウザAPIの差異(イベントリスナーの後片付けなど)を正しく処理する
- SSR(サーバーサイドレンダリング)環境でも安全に動くようにする
- TypeScriptの型定義まできちんと整備する
VueUseはこれらを踏まえて実装・テストされているため、自作するより信頼性が高く、コード量も減らせます。
私自身、以前はwindow.addEventListener('resize', ...)を自分でコンポーネントに書いて、onUnmountedでの解除を忘れてメモリリークのような現象に悩んだことがありました。
VueUseのuseWindowSizeを知ってからは、そうした後片付け漏れの心配自体がなくなりました。
VueUseの導入と基本の使い方
インストール
npm install @vueuse/core
代表的なComposableを使ってみる
まずは、ウィンドウサイズをリアクティブに取得するuseWindowSizeです。
<script setup lang="ts">
import { useWindowSize } from '@vueuse/core'
const { width, height } = useWindowSize()
</script>
<template>
<p>画面サイズ:{{ width }} × {{ height }}</p>
</template>
widthとheightはどちらもリアクティブな値なので、ウィンドウをリサイズすると自動的に画面の表示も更新されます。
自分でresizeイベントを登録して監視する必要はありません。
よく使うComposable3選
useLocalStorage:状態をローカルストレージと自動同期する
ダークモードの設定など、リロードしても保持したい値にはuseLocalStorageが便利です。
<script setup lang="ts">
import { useLocalStorage } from '@vueuse/core'
const isDarkMode = useLocalStorage('dark-mode', false)
const toggleDarkMode = () => {
isDarkMode.value = !isDarkMode.value
}
</script>
<template>
<button @click="toggleDarkMode">
{{ isDarkMode ? 'ライトモードにする' : 'ダークモードにする' }}
</button>
</template>
isDarkMode.valueを更新するだけで、通常のrefと同じ感覚でローカルストレージへの読み書きが自動的に行われます。
useDebounceFn:検索窓の入力を間引く
入力のたびにAPIを呼ぶと通信量が無駄になるため、入力が止まってから一定時間後に実行する「デバウンス」処理がよく使われます。
<script setup lang="ts">
import { ref } from 'vue'
import { useDebounceFn } from '@vueuse/core'
import { apiClient } from '@/lib/api'
const keyword = ref('')
const results = ref<{ id: number; title: string }[]>([])
const searchPosts = useDebounceFn(async () => {
const response = await apiClient.get('/posts', {
params: { q: keyword.value },
})
results.value = response.data
}, 500)
</script>
<template>
<input v-model="keyword" @input="searchPosts" placeholder="キーワードを入力" />
</template>
500ミリ秒の間、次の入力がなければ検索処理が実行される、という制御をたった1行のuseDebounceFnで実現できます。
useFetch:axiosより手軽なデータ取得
前回axiosで書いたようなデータ取得処理を、VueUseのuseFetchで書き直すこともできます。
<script setup lang="ts">
import { useFetch } from '@vueuse/core'
interface Post {
id: number
title: string
}
const { data: posts, isFetching, error } = useFetch('https://api.example.com/posts')
.json<Post[]>()
</script>
<template>
<p v-if="isFetching">読み込み中です...</p>
<p v-else-if="error">エラーが発生しました</p>
<ul v-else>
<li v-for="post in posts" :key="post.id">{{ post.title }}</li>
</ul>
</template>
data・isFetching・errorが最初からセットで返ってくるため、前回axiosで自作したローディング・エラー管理のコードを大幅に減らせます。
ただし、共通ヘッダーや複雑なインターセプター処理が必要な業務APIでは、axiosの方が柔軟なケースも多いため、状況に応じて使い分けるのがおすすめです。
よくあるつまずきポイント:SSR環境でのwindowアクセス
私が実際にハマった落とし穴
Nuxt.jsのようなSSR環境のプロジェクトで、windowオブジェクトを直接参照するComposableを自作していたとき、サーバー側でwindow is not definedというエラーに遭遇したことがあります。
ブラウザでしか動かない前提でコードを書いてしまい、サーバーサイドでレンダリングされるタイミングを考慮できていなかったのが原因でした。
❌ Before:windowを直接参照する自作Composable(SSR環境でエラーになる)
// composables/useWindowWidth.ts
import { ref, onMounted } from 'vue'
export function useWindowWidth() {
const width = ref(0)
// SSR環境ではこの時点でwindowが存在せずエラーになる
width.value = window.innerWidth
onMounted(() => {
window.addEventListener('resize', () => {
width.value = window.innerWidth
})
})
return { width }
}
✅ After:VueUseのuseWindowSizeに置き換える(SSR安全)
<script setup lang="ts">
import { useWindowSize } from '@vueuse/core'
// SSR環境かどうかを内部で判定し、安全にフォールバックしてくれる
const { width } = useWindowSize()
</script>
<template>
<p>現在の画面幅:{{ width }}px</p>
</template>
VueUseのComposableは、こうした「ブラウザ環境依存の処理」を内部で吸収してくれているため、SSR環境かどうかを自分で気にする必要がありません。
「ブラウザAPIを直接触りたくなったら、まずVueUseに同じ機能がないか探す」という習慣をつけておくと、こうした事故を未然に防げます。
まとめ
この記事のポイント
- VueUseはVue 3向けに整備された200以上のComposable集で、実務でよく使う処理を自作せずに済ませられる
useWindowSizeやuseLocalStorageなど、ブラウザAPIの後片付けまで含めて安全に実装されているuseDebounceFnで入力の間引き、useFetchで手軽なデータ取得ができる- 自作ComposableでSSR環境向けの考慮が漏れると
window is not definedのようなエラーにつながる - 「ブラウザAPIを触る前にVueUseを探す」という習慣が、コード量とバグの両方を減らす
次に読むべき記事
次回は「VeeValidateでフォームバリデーションを実装する」を解説します。
フォームまわりも、VueUseと同様に専用ライブラリを使うことで実装がぐっと楽になります。
→ 次の記事:VeeValidateでフォームバリデーションを実装する