- Vue.js
VueUseの使い方|よく使う30関数の早見表とサイズ実測【v15】
VueUseとは、Vue 3のComposition APIで使える便利な関数(composable)を200以上まとめたライブラリです。 useLocalStorage で入力内容をブラウザに保存する、onClickOutside でメニューの外側クリックを検知する、useDark でダークモードを切り替える、といった「きちんと書くと意外に長い処理」を1行で呼び出せます。
この記事では、2026年9月16日に公開された VueUse 15.0.0(@vueuse/core) と Vue 3.5.43、Vite 8.3.1 の組み合わせで、よく使う30関数をカテゴリ別の早見表とコピペで動くコードにまとめました。さらに上位記事にはない次の3点を、実際に動かした結果つきで載せています。
- 関数を1つ使うごとにバンドルが何バイト増えるかをViteでビルドして実測(tree-shakingが効くことの確認と、自作との比較)
- Chromium(Playwright)で画面を操作して、
onClickOutside・useLocalStorage・useDarkなどの動きを確認 - v15の変更点(
useNowの更新間隔の指定方法など)と、SSR・Nuxtでの注意点
VueUseのよく使う関数 早見表(30選)
「公式カテゴリ」は VueUse のドキュメントでの分類です。すべて import { 関数名 } from '@vueuse/core' で読み込めます。
| やりたいこと | 関数 | 公式カテゴリ | 最小コード |
|---|---|---|---|
| localStorageに保存・復元 | useLocalStorage |
State | const memo = useLocalStorage('memo', '') |
| sessionStorageなど保存先を選ぶ | useStorage |
State | useStorage('k', {}, sessionStorage) |
| 元に戻す・やり直す | useRefHistory |
State | const { undo, redo } = useRefHistory(text) |
| composableを全体で1つだけ共有 | createSharedComposable |
State | export const useSharedMouse = createSharedComposable(useMouse) |
| 要素の幅・高さを知る | useElementSize |
Elements | const { width, height } = useElementSize(el) |
| 要素が画面に入ったら処理 | useIntersectionObserver |
Elements | useIntersectionObserver(el, ([e]) => {...}) |
| 要素が見えているかを真偽値で | useElementVisibility |
Elements | const visible = useElementVisibility(el) |
| ウィンドウの幅・高さ | useWindowSize |
Elements | const { width } = useWindowSize() |
| クリップボードにコピー | useClipboard |
Browser | const { copy, copied } = useClipboard() |
| タブのタイトルを変える | useTitle |
Browser | const title = useTitle('一覧') |
| ダークモード切り替え | useDark |
Browser | const isDark = useDark() |
| メディアクエリの判定 | useMediaQuery |
Browser | useMediaQuery('(min-width: 768px)') |
| ブレークポイントで判定 | useBreakpoints |
Browser | useBreakpoints(breakpointsTailwind).greaterOrEqual('md') |
| イベントを登録(自動で解除) | useEventListener |
Browser | useEventListener(window, 'resize', fn) |
| マウス座標 | useMouse |
Sensors | const { x, y } = useMouse() |
| 要素の外側クリックを検知 | onClickOutside |
Sensors | onClickOutside(menu, () => { open.value = false }) |
| スクロール位置・端に着いたか | useScroll |
Sensors | const { y, arrivedState } = useScroll(el) |
| キー入力を検知 | onKeyStroke |
Sensors | onKeyStroke('Escape', () => {...}) |
| 無限スクロール | useInfiniteScroll |
Sensors | useInfiniteScroll(el, loadMore, { distance: 10 }) |
| オンライン/オフライン | useOnline |
Sensors | const online = useOnline() |
| APIを呼ぶ(状態つき) | useFetch |
Network | const { data, error } = useFetch(url).json() |
| true/falseを切り替え | useToggle |
Utilities | const [open, toggle] = useToggle() |
| 関数を間引く(最後の1回だけ) | useDebounceFn |
Utilities | const search = useDebounceFn(fn, 300) |
| 関数を一定間隔に制限 | useThrottleFn |
Utilities | const onScroll = useThrottleFn(fn, 200) |
| 値の変化を遅らせたrefを作る | refDebounced |
Reactivity | const debounced = refDebounced(keyword, 300) |
| 値の変化を間引いてwatch | watchDebounced |
Watch | watchDebounced(keyword, fn, { debounce: 300 }) |
| 一定間隔で処理(止める・再開) | useIntervalFn |
Animation | const { pause, resume } = useIntervalFn(fn, 1000) |
| 現在時刻をリアクティブに | useNow |
Animation | const now = useNow() |
| 日付を書式化 | useDateFormat |
Time | useDateFormat(date, 'YYYY/MM/DD') |
| 「5分前」の表示 | useTimeAgo / useTimeAgoIntl |
Time | useTimeAgoIntl(date, { locale: 'ja' }) |
迷ったら「保存は State、DOM要素は Elements、ブラウザ機能は Browser、ユーザー操作は Sensors」と覚えると探しやすくなります。
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「VueUseの使い方|よく使う30関数の早見表とサイズ実測【v15】」
VueUseを使う準備(インストールと前提)
VueUseはnpmでインストールし、使う関数だけを名前付きimportで読み込みます。
npm create vite@latest my-app -- --template vue
cd my-app
npm i @vueuse/core
<script setup>
import { useMouse } from '@vueuse/core'
const { x, y } = useMouse()
</script>
<template>
<p>マウス座標: {{ x }}, {{ y }}</p>
</template>
今回の検証環境は次のとおりです。
| 項目 | バージョン |
|---|---|
| @vueuse/core | 15.0.0(2026-09-16公開) |
| vue | 3.5.43 |
| vite / @vitejs/plugin-vue | 8.3.1 / 6.0.9 |
| Node.js | 22.22.2 |
| ブラウザ | Chromium(Playwright経由) |
この記事の vue コードブロック(11個)は、すべて上の環境でビルドし、Chromiumで表示して警告・エラーが出ないことを確認しています。 「実行結果」として載せた出力は、Playwrightで実際に操作して得たものです。
前提条件は「Vue 3.5以上」と「Node.js 22以上(ビルド環境)」です。 npmに登録された @vueuse/core@15.0.0 の peerDependencies は vue: ^3.5.0、engines は node: >=22 でした。
Vue 2のプロジェクトで使う場合
VueUse 12.0以降はVue 3専用です。 公式ガイドには「Vue 2をサポートするにはv11.xを使う」と案内されています。Vue 2(2.7)のプロジェクトは長く現場を支えてきた実績があり、すぐに移行しない判断にも、安定稼働や改修コストといった理由があります。その場合は VueUse 11系を固定して使うと、Composition APIの恩恵を受けながら今の環境を保てます。
v15(2026年9月)の変更点
v14からv15への変更は「使い方が変わる箇所が少しある」程度です。 GitHubのリリースノートに載っている破壊的変更は次のとおりです。
| 変更 | 内容 | 対応 |
|---|---|---|
templateRef の削除 |
テンプレート参照用の関数がなくなった | Vue 3.5の useTemplateRef を使う |
| Node.js 20のサポート終了 | ビルド環境のNode要件が上がった | Node.js 22以上にする |
| タイマー系の非推奨オプション削除 | useNow などの更新間隔は scheduler で指定 |
下のコード例を参照 |
useThrottleFn の既定値 |
trailing の既定が false → true |
最後の1回を実行したくないなら useThrottleFn(fn, 500, false) |
useEventSource / useIDBKeyval |
SSEの受信処理、タブ間同期の動作が変わった | 使っている場合はリリースノートで確認 |
v14(2025年10月)では、Vue 3.5以上が必須になり、別名(alias)でのexportが非推奨になっています。v13以前から上げる場合はv14の変更点もあわせて確認します。
useNowの更新間隔は scheduler で指定する
useNow は既定で requestAnimationFrame ごと(画面の描画ごと)に更新されます。1秒ごとでよい場合は、scheduler に useIntervalFn を渡します。
<script setup>
import { useNow, useIntervalFn, useDateFormat } from '@vueuse/core'
// 1秒ごとに現在時刻を更新する(v15の書き方)
const now = useNow({ scheduler: (cb) => useIntervalFn(cb, 1000) })
const label = useDateFormat(now, 'HH:mm:ss')
</script>
<template>
<p>{{ label }}</p>
</template>
Chromiumで3.5秒待った結果、更新回数は3回でした(1秒ごとに更新されている)。
カテゴリ別:コピペで使えるコード例
State:入力内容を保存する(useLocalStorage / useStorage)
useLocalStorage は、refと同じ感覚で使えて、値を変えると自動でlocalStorageに保存されます。 初期値の型(文字列・数値・オブジェクトなど)を見て、保存時の変換(JSONへの変換など)も自動で選ばれます。
<script setup>
import { useLocalStorage, useStorage } from '@vueuse/core'
const memo = useLocalStorage('demo-memo', '')
// オブジェクトもそのまま保存できる(JSONに変換される)
const settings = useLocalStorage('settings', { pageSize: 20, showDone: true })
// 保存先を sessionStorage にする
const draft = useStorage('draft', '', sessionStorage)
</script>
<template>
<textarea id="memo" v-model="memo"></textarea>
<label><input type="checkbox" v-model="settings.showDone" /> 完了も表示</label>
</template>
Playwrightでの実行結果(入力 → localStorageを確認 → 再読み込み):
localStorage: 備品の発注を確認する
再読み込み後のtextarea: 備品の発注を確認する
同じキーを使う別タブの変更も反映されます(storage イベントを監視しているため)。これは自作の数行版にはない機能です。
Sensors:メニューの外側クリックで閉じる(onClickOutside)
onClickOutside(要素, 処理) で、要素の外側がクリックされたときだけ処理が走ります。 ドロップダウンやモーダルでよく使います。
<script setup>
import { ref, useTemplateRef } from 'vue'
import { onClickOutside } from '@vueuse/core'
const open = ref(false)
const menu = useTemplateRef('menu')
onClickOutside(menu, () => { open.value = false })
</script>
<template>
<div ref="menu">
<button id="menu-btn" @click="open = !open">メニュー</button>
<ul v-if="open" id="menu-list"><li>設定</li><li>ログアウト</li></ul>
</div>
<p id="outside">ここは外側です</p>
</template>
実行結果(ボタン → メニュー内をクリック → 外側をクリック):
メニュー表示: true
内側クリック後: true
外側クリック後: false
判定対象は ref="menu" を付けた要素の中かどうかです。ボタンをメニューの外に置くと、ボタンを押した瞬間に「外側クリック」と判定されて開閉がちらつくことがあります。ボタンも同じ要素の中に入れるか、ignore オプションで除外します。
Browser:ダークモードを切り替える(useDark / useToggle)
useDark は <html> に dark クラスを付け外しし、選んだ状態をlocalStorageに保存します。 初回はOSの設定(prefers-color-scheme)に従います。
<script setup>
import { useDark, useToggle } from '@vueuse/core'
const isDark = useDark()
const toggleDark = useToggle(isDark)
</script>
<template>
<button id="dark" @click="toggleDark()">{{ isDark ? 'ライト' : 'ダーク' }}にする</button>
</template>
<style>
html.dark { background: #1e1e1e; color: #eee; }
</style>
実行結果:
html class(前): null
html class(後): dark / 保存値: dark
再読み込み後: dark / ボタン: ライトにする
保存キーは vueuse-color-scheme でした。Tailwind CSSの dark: を「classで切り替える」設定にしている場合、そのまま連携できます。
Utilities:検索の入力を間引く(useDebounceFn / refDebounced / watchDebounced)
入力のたびにAPIを呼ぶと回数が増えるため、「入力が止まって300ms後に1回だけ」実行します。
<script setup>
import { ref } from 'vue'
import { useDebounceFn, refDebounced, watchDebounced } from '@vueuse/core'
const keyword = ref('')
const searchCount = ref(0)
// ① 関数を間引く
const search = useDebounceFn(() => { searchCount.value++ }, 300)
// ② 値を間引いたrefを作る(computedやテンプレートで使う)
const debouncedKeyword = refDebounced(keyword, 300)
// ③ watchを間引く(maxWaitで最大待ち時間も指定できる)
watchDebounced(keyword, (v) => console.log('検索:', v), { debounce: 300, maxWait: 1000 })
</script>
<template>
<input id="kw" v-model="keyword" @input="search" />
<p id="count">{{ searchCount }}</p>
</template>
実行結果(50ms間隔で notebook の8文字を入力し、500ms待つ):
8文字入力後の検索回数: 1
useThrottleFn はv15で「最後の1回も実行する(trailing: true)」が既定になりました。 500msのスロットルで5回連打したところ、直後は1回、0.8秒後には2回になりました(最初の1回+最後の1回)。
Elements:要素の大きさと表示状態(useElementSize / useIntersectionObserver)
遅延読み込みなど「一度見えたら処理」は useIntersectionObserver、表示の出し分けだけなら useElementVisibility が手軽です。
<script setup>
import { ref, useTemplateRef } from 'vue'
import { useElementSize, useIntersectionObserver, useElementVisibility } from '@vueuse/core'
const box = useTemplateRef('box')
const { width, height } = useElementSize(box)
const target = useTemplateRef('target')
const seen = ref(false)
// 一度でも画面に入ったら true にして監視をやめる
const { stop } = useIntersectionObserver(target, ([entry]) => {
if (entry.isIntersecting) { seen.value = true; stop() }
})
const visible = useElementVisibility(target) // 今見えているか
</script>
<template>
<div ref="box">{{ Math.round(width) }} × {{ Math.round(height) }}</div>
<div ref="target">{{ seen ? '表示されました' : '' }} / 見えている: {{ visible }}</div>
</template>
Browser:コピー・タイトル・画面幅(useClipboard / useTitle / useMediaQuery / useBreakpoints)
<script setup>
import { computed } from 'vue'
import { useClipboard, useTitle, useMediaQuery, useBreakpoints, breakpointsTailwind } from '@vueuse/core'
const orderNo = 'A-20260928-001'
const { copy, copied, isSupported } = useClipboard({ copiedDuring: 1500 })
const count = 3
useTitle(computed(() => `未読 ${count} 件 | タスク管理`))
const isWide = useMediaQuery('(min-width: 768px)')
const bp = useBreakpoints(breakpointsTailwind)
const isMdUp = bp.greaterOrEqual('md')
</script>
<template>
<button v-if="isSupported" @click="copy(orderNo)">{{ copied ? 'コピーしました' : '注文番号をコピー' }}</button>
<p>{{ isWide ? 'PC表示' : 'スマホ表示' }} / md以上: {{ isMdUp }}</p>
</template>
useClipboard はHTTPSまたはlocalhostでのみ動きます(ブラウザのClipboard APIの制約)。isSupported で表示を出し分けると安全です。
Sensors:スクロール・キー操作・無限スクロール
<script setup>
import { ref, useTemplateRef } from 'vue'
import { useScroll, onKeyStroke, useInfiniteScroll } from '@vueuse/core'
const list = useTemplateRef('list')
const { y, arrivedState } = useScroll(list)
const items = ref(Array.from({ length: 20 }, (_, i) => `記事 ${i + 1}`))
const hasMore = ref(true)
useInfiniteScroll(list, async () => {
const next = items.value.length
items.value.push(...Array.from({ length: 10 }, (_, i) => `記事 ${next + i + 1}`))
if (items.value.length >= 100) hasMore.value = false
}, { distance: 10, canLoadMore: () => hasMore.value })
const open = ref(true)
onKeyStroke('Escape', () => { open.value = false })
</script>
<template>
<p>位置: {{ Math.round(y) }} / 下端: {{ arrivedState.bottom }}</p>
<ul ref="list" style="height: 300px; overflow-y: auto">
<li v-for="item in items" :key="item">{{ item }}</li>
</ul>
</template>
canLoadMore を返さないと、最後まで読み込んだあとも呼ばれ続けます。 件数やAPIの「次ページなし」で止めるようにします。
Network:APIを呼ぶ(useFetch)
<script setup>
import { ref, computed } from 'vue'
import { useFetch } from '@vueuse/core'
const page = ref(1)
const url = computed(() => `/api/articles?page=${page.value}`)
// refetch: true で url が変わるたびに再取得
const { data, error, isFetching } = useFetch(url, { refetch: true }).get().json()
</script>
<template>
<p v-if="isFetching">読み込み中…</p>
<p v-else-if="error">取得に失敗しました</p>
<ul v-else><li v-for="a in data" :key="a.id">{{ a.title }}</li></ul>
<button @click="page++">次のページ</button>
</template>
共通のベースURLや認証ヘッダーを付けたいときは createFetch({ baseUrl, options: { beforeFetch } }) で専用の useFetch を作ります。 Nuxtでは同名の useFetch がNuxt側にあるため、後述のとおりVueUse版は自動importされません。
Time・State:時刻・履歴・共有
<script setup>
import { ref } from 'vue'
import { useDateFormat, useTimeAgoIntl, useRefHistory } from '@vueuse/core'
const created = new Date(2026, 8, 28, 9, 5)
const dateLabel = useDateFormat(created, 'YYYY/MM/DD(ddd) HH:mm', { locales: 'ja-JP' })
const ago = useTimeAgoIntl(created, { locale: 'ja' })
const text = ref('')
const { undo, redo, canUndo, canRedo } = useRefHistory(text, { capacity: 20 })
</script>
<template>
<p>{{ dateLabel }}({{ ago }})</p>
<input v-model="text" />
<button :disabled="!canUndo" @click="undo()">元に戻す</button>
<button :disabled="!canRedo" @click="redo()">やり直す</button>
</template>
実行結果(日付の書式はChromium、相対時間は「5分前の日時」をNode.jsで渡した結果):
日付: 2026/09/28(月) 09:05
useTimeAgo: 5 minutes ago
useTimeAgoIntl: 5 分前
useTimeAgo の既定は英語表記です。日本語で表示したい場合は、ブラウザの Intl.RelativeTimeFormat を使う useTimeAgoIntl が手軽でした。
関数を1つ使うと何バイト増える?(Viteで実測)
VueUseは使った関数だけがバンドルに入ります(tree-shaking)。 「200以上の関数が入ったライブラリを入れると重くなるのでは」と気になる方のために、実際に測りました。
測り方
ref(0)を表示するだけの最小のVueアプリを「基準」としてビルド- 基準に関数を1つだけ追加したアプリを関数ごとに作ってビルド
- 出力されたJSを
gzip -9相当で圧縮したサイズを比べる
(Vite 8.3.1 の本番ビルド、@vueuse/core 15.0.0。基準のJSはgzipで23,850バイトで、ほぼVue本体の大きさです)
実測結果
| 追加した関数 | 増えたサイズ(gzip) |
|---|---|
useToggle |
+134 B |
useEventListener |
+257 B |
useIntervalFn |
+283 B |
useThrottleFn |
+289 B |
useNow |
+420 B |
useDebounceFn |
+443 B |
useIntersectionObserver |
+550 B |
useDateFormat |
+716 B |
useMouse |
+824 B |
useMediaQuery |
+860 B |
useElementSize |
+872 B |
useTitle |
+995 B |
onClickOutside |
+1,078 B |
useClipboard |
+1,371 B |
useBreakpoints |
+1,429 B |
useStorage |
+2,022 B |
useLocalStorage |
+2,041 B |
useScroll |
+2,075 B |
useFetch |
+2,434 B |
useInfiniteScroll |
+2,751 B |
useDark |
+3,162 B |
| 上の22関数をまとめて使う | +11,264 B |
import * as VueUse で全部読み込む |
+49,335 B |
1関数あたり数百バイト〜3KB程度で、22関数をまとめて使っても約11KBでした。 個別の増加分を足すと約25KBになりますが、まとめると内部の共通処理(イベント登録やストレージ処理など)が共有されるため半分以下に収まっています。
一方、import * as VueUse from '@vueuse/core' のように名前空間ごと読み込んで使うと、約49KB増えました。 使う関数を名前付きで import { useMouse } from '@vueuse/core' と書けば、この差は出ません。
自作した場合と比べると
同じ目的の処理を最小限のコードで自作し、同じ方法で測りました。
| 処理 | VueUse | 自作(最小限) | VueUseに含まれている主な処理 |
|---|---|---|---|
| localStorageに保存 | +2,041 B | +34 B | SSRで落ちない、型に合わせた変換、別タブとの同期、エラー処理 |
| 外側クリック検知 | +1,078 B | +99 B | iframe・ドラッグ操作への配慮、除外要素(ignore)、解除関数 |
| デバウンス | +443 B | ほぼ0 B | maxWait、Promiseを返す、引数の受け渡し |
差の分だけ「端のケース」への対応が入っていると考えると判断しやすくなります。 たとえば自作のlocalStorage版は、次の章のとおりSSRで localStorage is not defined になります。
気をつけたい点:戻り値を使わない useNow はビルドで消える
検証中、const now = useNow({...}) の now をテンプレートで使っていないと、本番ビルドで useNow の呼び出しごと消えて、コールバックが1回も動きませんでした(更新回数0回)。テンプレートで now を表示すると3回に戻りました。
VueUseのソースを見ると、useNow などの一部の関数には /* @__NO_SIDE_EFFECTS__ */(戻り値を使わなければ削除してよい、という目印) が付いています。「戻り値を使わず、副作用だけが目的」なら useIntervalFn のように処理を直接実行する関数を使うと、意図どおりに動きます。
SSR・Nuxtで使うときの注意点
SSRでは window がないので、初期値が返る
VueUseの関数は、サーバー側(window がない環境)で呼ばれてもエラーにならず、初期値を返すように作られています。 Vueの renderToString で確かめた結果です。
import { createSSRApp, h, ref } from 'vue'
import { renderToString } from 'vue/server-renderer'
import { useLocalStorage, useDark, useWindowSize, useMediaQuery } from '@vueuse/core'
const WithVueUse = { setup() {
const memo = useLocalStorage('memo', '(未入力)')
const isDark = useDark()
const { width } = useWindowSize()
const wide = useMediaQuery('(min-width: 768px)')
return () => h('p', `memo=${memo.value} dark=${isDark.value} width=${width.value} wide=${wide.value}`)
} }
const Handmade = { setup() {
const memo = ref(localStorage.getItem('memo') ?? '')
return () => h('p', memo.value)
} }
console.log('VueUse:', await renderToString(createSSRApp(WithVueUse)))
try { await renderToString(createSSRApp(Handmade)) } catch (e) { console.log('自作:', e.constructor.name + ': ' + e.message) }
VueUse: <p>memo=(未入力) dark=false width=Infinity wide=false</p>
[Vue warn]: Unhandled error during execution of setup function
自作: ReferenceError: localStorage is not defined
サーバー側では useWindowSize の幅が Infinity、useMediaQuery は false になりました。 この値でサーバーが描画し、ブラウザ側で実際の値に変わるため、画面幅で見た目を大きく変える部分は、描画の食い違い(hydration mismatch)やちらつきが起きやすくなります。 次のように対策します。
| 起きること | 対策 |
|---|---|
| 画面幅で表示を切り替えると、サーバーとブラウザで内容が違う | 見た目の切り替えはCSSのメディアクエリで行う/<ClientOnly> で囲む |
useWindowSize の初期幅が Infinity |
useWindowSize({ initialWidth: 1024 }) のように初期値を渡す |
| ダークモードが読み込み直後に一瞬ライトになる | Nuxtでは ssrHandlers を有効にする(下記)、または @nuxtjs/color-mode を使う |
| localStorageの値がサーバーでは読めない | 初期値で描画される前提で作る/Cookieに保存する |
Nuxtでは @vueuse/nuxt を入れる
Nuxtでは @vueuse/nuxt モジュールを入れると、VueUseの関数が自動importされ、import を書かずに使えます。
npx nuxt@latest module add vueuse
手動で入れる場合は npm i -D @vueuse/nuxt @vueuse/core を実行し、設定ファイルに追加します。
// nuxt.config.ts
export default defineNuxtConfig({
modules: ['@vueuse/nuxt'],
vueuse: {
ssrHandlers: true, // localStorage系の保存先をCookieにし、html属性をuseHeadで出力
},
})
@vueuse/nuxt 15.0.0 のソースで確認した動きは次のとおりです(Nuxtアプリ上での実行はしていません)。
| 項目 | 内容 |
|---|---|
| 自動importされない関数 | useFetch・useCookie・useHead・useStorage・useImage・toRef・toRefs・toValue(Nuxt・Vueの同名関数を優先) |
@nuxtjs/color-mode があるとき |
useColorMode も自動importの対象外になる |
ssrHandlers: true |
ストレージの保存先をCookie(useCookie)にし、useDark などによる <html> の属性をサーバー側で useHead で出力 |
| 対応Nuxt | peerDependencies は nuxt: ^3.0.0 || ^4.0.0-0 || ^5.0.0-0 |
useFetch はNuxtの同名関数(サーバーでの取得と結果の受け渡しに対応)が使われます。 VueUse版を使いたいときは import { useFetch } from '@vueuse/core' と明示します。
よくあるつまずきと直し方
| 症状 | 原因 | 直し方 |
|---|---|---|
onClickOutside でボタンを押すと即閉じる |
ボタンが判定対象の要素の外にある | ボタンを同じ要素に入れる/ignore で除外 |
| バンドルが大きい | import * や名前空間で使っている |
名前付きimportにする |
| SSRで表示がずれる | サーバーでは初期値で描画される | CSSで切り替える/<ClientOnly>/初期値を渡す |
| コールバックが動かない | 戻り値を使わない関数がビルドで削除された | 戻り値を使う/処理を直接実行する関数を使う |
よくある質問
Q. VueUseは本番のプロジェクトで使っても大丈夫ですか?
A. 使った関数だけがバンドルに入り、今回の実測では1関数あたり数百バイト〜3KB程度でした。@vueuse/core は継続的にリリースされており、今回の最新版は2026年9月公開のv15.0.0です。導入時はメジャーバージョンを固定し、更新時にリリースノートの破壊的変更を確認するのがおすすめです。
Q. Vue 2のプロジェクトでもVueUseは使えますか? A. VueUse 12以降はVue 3専用です。Vue 2.7のプロジェクトでは、公式ガイドの案内どおりVueUse 11系を使うとComposition APIと組み合わせて利用できます。
Q. VueUseの関数と自作のcomposable、どちらを使えばよいですか? A. イベント登録の解除やSSR対応など「端のケース」が多い処理はVueUseを、業務ロジック(注文の合計計算など)は自作のcomposableにするのが基本です。VueUseの関数を組み合わせて自作composableを作る書き方もよく使われます。
まとめ
- VueUseはVue 3用の便利関数集。
import { 関数名 } from '@vueuse/core'で使いたいものだけ読み込む - よく使うのは State(保存)・Elements(要素)・Browser(ブラウザ機能)・Sensors(操作) の4カテゴリ
- v15(2026年9月) は
templateRef削除、Node 22以上、useNowなどの更新間隔はschedulerで指定、useThrottleFnは trailingが既定でtrue - 実測で1関数あたり+134B〜+3.2KB(gzip)、22関数で+11KB。名前空間ごとの読み込みは+49KBになる
- 自作より大きい分、SSRで落ちない・後片付け・タブ間同期などが入っている
- SSRでは初期値で描画されるので、画面幅やダークモードの扱いに注意。Nuxtでは
@vueuse/nuxtを使う - Vue 2のプロジェクトは VueUse 11系 で、今の環境のまま恩恵を受けられる
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「VueUseの使い方|よく使う30関数の早見表とサイズ実測【v15】」
株式会社RJCでは、VueUseのような便利なライブラリも「どこまで任せて、どこを自分たちで書くか」を実測しながら判断し、プロジェクトの中で共有しています。こうした知識を共有しながら、日々技術を磨いています。
株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。
WE ARE HIRING
RJCで一緒に開発しながら、
成長を楽しみませんか?
RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!
- 127日年間休日
- 12時間平均残業時間
- 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!
ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。