VueUseはVue2/Vue3のComposition APIを使った場合に利用することができる便利な関数を集めたライブラリです。Composition APIではコンポーネントとは別にreactiveな変数とロジックを分けることで再利用な関数を作成することができるためVueUseのようなライブラリを作成することができます。

VueのComposition APIではVueUseに含まれるような再利用可能な関数のことをComposablesな関数と呼びます。Composablesという名前だけではどのようなものか分かりにいので本文書ではVueUseの利用方法とComposablesな関数の作成方法を説明しています。さらに関数だけではなくComposablesなコンポーネントの作成方法も説明しています。

VueUseの関数を見ていくと自作できそうなものも存在しますが100以上の関数が登録されているので今まで利用した経験がない人であれば利用可能な関数がないか一度目を通してみることをお勧めします。

VueUseの使い方よりもVueのComposablesの基本についての説明が主になっています。

Vue.jsのComposablesはReactのHooksに対応します。

Vue 3の環境構築

VueUseの動作確認を行うためにVue 3のプロジェクトの作成を行います。プロジェクトの作成にはViteは利用します。下記のコマンドを実行することでvueuse-appという名前のフォルダが作成されます。


 % npm init vite@latest vueuse-app -- --template vue

作成されたvueuse-appに移動してnpm installコマンドを実行します。


 % cd vueuse-app 
 % npm install
ViteではなくVueのビルドツールを利用したい場合はnpm init vue@latestのコマンドを実行してください。公式のプロジェクトの作成ツールのcreate-vueが実行され利用したい機能を選択してインストールを行うことができます。

npm installによるJavaScriptライブラリのインストール後にnpm run devコマンドを実行します。開発サーバが起動するのでhttp://localhost:5173アクセスすることができます。


 % npm run dev
//略

  VITE v3.0.4  ready in 426 ms

  ➜  Local:   http://localhost:5173/
  ➜  Network: use --host to expose
Vue+Viteの初期画面
Vue+Viteの初期画面

VueUseライブラリのインストール

Viteによるプロジェクトの作成と開発サーバの起動が確認できたのでVueUseライブラリのインストールを行います。


 % npm i @vueuse/core

VueUseにどのような関数があるのか確認を行うためVueUseのサイトにアクセスします。画面中央にあるFunctionsをクリックすると関数の一覧ページに移動することができます。

VueUseのホームページ
VueUseのホームページ

本文書執筆時には142の関数が登録されていますが日々関数の数は増えています。VueUseのベースとなるCoreに関しては用途に応じて大きく12個のカテゴリーに分かれています。Browserのカテゴリーにはイベントの追加・削除に利用できるuseEventListenerやElementsのカテゴリーには要素の移動に利用できるuseDraggableなどがあります。ほとんどの関数の名前の前にはuseがついています。

VueUseの関数一覧
VueUseの関数一覧

各関数のページにはデモ、利用例が記述されているので利用するためのハードルは高くありません。ページにはソースコードへのリンクもあり、それぞれの関数がどのように記述されているか読みこともスキル向上につながると思うので気になる関数はソースコードを読んでみることをお勧めします。

useDraggableのページ
useDraggableのページ

useCounter

シンプルな関数はたくさんありますがComposablesな関数を自作するための教材としてはuseCounter関数は非常にわかりやすいので今回はuseCounterを利用して動作確認を行っていきます。

VueUseの利用

ドキュメントの中には使用方法もオプションの利用方法も型の定義も記述されているので参考に設定を行っていきます。

App.vueファイルにvueuseのcoreからuseCounter関数をimportして動作確認を行ってみましょう。useCounter関数を利用することでカウンター機能を持たせることができます。頻繁に学習用に利用される機能なのでほとんどの方が作ったことがあるとは思います。ボタンをクリックするとカウント数が増えたり減ったりするカウンターです。

useCounterの引数にはカウントの初期値を指定することができ、戻り値の中からcount変数, inc関数, dec関数を利用しています。VueUseではimportした関数を実行し戻される変数や関数を利用することでimportしたコンポーネントでimportした関数が持つ機能を利用できるようになります。その他useCounterではget, resetなどの関数も戻されますが今回は利用しません。


<script setup>
import { useCounter } from '@vueuse/core';
const { count, inc, dec } = useCounter(0);
</script>

<template>
  <h1>VueUse: useCounter</h1>
  <div>Count:{{ count }}</div>
  <div>
    <button @click="() => inc()">increase</button>
    <button @click="() => dec()">decrease</button>
  </div>
</template>

countやinc, dec関数はAppコンポーネントで定義した場合と同様の方法で利用することができるのでincreaseボタンをクリックするとinc関数が実行されcountの数が増え、decreaseボタンをクリックするとdec関数が実行されcountの数が減ります。

useCounterを利用したカウント
useCounterを利用したカウント

useCounterの使用方法と動作確認を行うことができました。

Composablesな関数

このカウンター機能を自作することでcomposablesな関数の作成方法を確認していきます。

VueUseの利用せずApp.vueファイルにカウンター機能を追加するために以下のコードを記述します。ref関数でcountを定義し、inc関数でcountを1増やし、dec関数でcountを1減らせるように関数を設定します。動作確認するとVueUseで利用したuseCounterと同じ動作になります。


<script setup>
import { ref } from 'vue';

const count = ref(0);
const inc = () => count.value = count.value + 1;
const dec = () => count.value = count.value - 1;
</script>

<template>
  <h1>VueUse: useCounter</h1>
  <div>Count:{{ count }}</div>
  <div>
    <button @click="() => inc()">increase</button>
    <button @click="() => dec()">decrease</button>
  </div>
</template>

上記のコードを利用してcomposablesな関数を作成します。srcフォルダの下にcomposablesフォルダを作成してuseCounter.jsファイルを作成してださい。

useCounter.jsファイルではcount変数とinc, dec関数の設定を行いVueのコンポーネントからimportできるようにexportを行います。この設定によりimportしたコンポーネントでuseCounter関数を実行するとcount, inc, decが戻されます。これでcomposablesな関数の作成は完了です。


import { ref } from 'vue';

export function useCounter() {
  const count = ref(0);
  const inc = () => (count.value = count.value + 1);
  const dec = () => (count.value = count.value - 1);
  return {
    count,
    inc,
    dec,
  };
}

作成したuseCounter.jsファイルをimportして動作確認を行いましょう。VueUseを利用した場合と設定方法と関数名は同じですがimportする場所が異なるので注意してください。今回はVueUseとは異なり先ほど作成したcomposablesフォルダのuseCounter.jsファイルです。


<script setup>
import { useCounter } from './composables/useCounter';
const { count, inc, dec } = useCounter();
</script>

<template>
  <h1>VueUse: useCounter</h1>
  <div>Count:{{ count }}</div>
  <div>
    <button @click="() => inc()">increase</button>
    <button @click="() => dec()">decrease</button>
  </div>
</template>

increaseボタンをクリックするとCountの数が増え、decreaseボタンをクリックするとCountの数が減ります。

useCounterを利用したカウント
useCounterを利用したカウント

シンプルな関数ですが自作のcomposablesな関数の作成方法を理解することができました。

VueUseのuseCounterのソースコードを確認して作成方法に違いがないか確認しておきましょう。ソースコードはTypeScriptで記述されているので異なるように見えるかもしれませんが読んでみると先ほど自作した内容と作り方は同じであることがわかります。


import { ref } from 'vue-demi'

export interface UseCounterOptions {
  min?: number
  max?: number
}

/**
 * Basic counter with utility functions.
 *
 * @see https://vueuse.org/useCounter
 * @param [initialValue=0]
 * @param {Object} options
 */
export function useCounter(initialValue = 0, options: UseCounterOptions = {}) {
  const count = ref(initialValue)

  const {
    max = Infinity,
    min = -Infinity,
  } = options

  const inc = (delta = 1) => count.value = Math.min(max, count.value + delta)
  const dec = (delta = 1) => count.value = Math.max(min, count.value - delta)
  const get = () => count.value
  const set = (val: number) => (count.value = val)
  const reset = (val = initialValue) => {
    initialValue = val
    return set(val)
  }

  return { count, inc, dec, get, set, reset }
}

気になる違いとしてはVue2/Vue3でも対応できるように先頭ではref関数をvueではなくvue-demiを使ってimportしています。

Composablesなコンポーネント

VueUseで利用できる関数の中で一部のものは関数だけではなくrenderlessなコンポーネントバージョンを提供しています。これを参考にuseCounter関数をコンポーネント化してみましょう。renderlessはtemplateタグを持たないコンポーネントです。

composablesの関数を利用した場合はcomposablesの関数からimportしているコンポーネントに対してreactiveなデータと関数を渡しました。コンポーネントの場合もcomposablesなコンポーネントから親コンポーネントにreactiveなデータと関数を渡す必要があります。Composablesなコンポーネントでは子コンポーネントから親コンポーネントにデータを渡したい時に使うScoped Slotsを利用します。

Scoped Slotsがわからない人は公開済みの下記の文書が参考になります。

srcのcomponentsフォルダにUseCounter.vueファイルを作成し以下のコードを記述します。setup関数では引数にpropsとcontextが入っているのでcontextに含まれるslotsを利用します。slotsのdefaultの引数に渡したいデータと関数を設定しています。変数の定義や関数の設定は先ほどの関数の場合と変わりません。


<script>
import { ref } from "vue";

export default {
  setup(props, context) {
    const count = ref(0);
    const inc = () => (count.value = count.value + 1);
    const dec = () => (count.value = count.value - 1);

    return () =>
      context.slots.default({
        count,
        inc,
        dec,
      });
  },
};
</script>

分割代入を利用してcontextからslotsのみ取り出して利用することも可能です。


<script>
import { ref } from "vue";

export default {
  setup(props, { slots }) {
    const count = ref(0);
    const inc = () => (count.value = count.value + 1);
    const dec = () => (count.value = count.value - 1);

    return () =>
      slots.default({
        count,
        inc,
        dec,
      });
  },
};
</script>

App.vueファイル側ではScoped Slotを利用してv-slot:defaultでUseConterコンポーネントからデータを受け取ることができます。slotPropsの中にcount, inc, decが含まれているのでそれを利用します。


<script setup>
import UseCounter from './components/UseCounter.vue';
</script>

<template>
  <h1>VueUse: useCount</h1>
  <use-counter v-slot:default="slotProps">
    <div>Count:{{ slotProps.count }}</div>
    <div>
      <button @click="() => slotProps.inc()">increase</button>
      <button @click="() => slotProps.dec()">decrease</button>
    </div>
  </use-counter>
</template>

slotPropsを分割代入して変数と関数を取り出すことができこちらのほうがコードがすっきりします。


<script setup>
import UseCounter from './components/UseCounter.vue';
</script>

<template>
  <h1>VueUse: useCount</h1>
  <use-counter v-slot:default="{ count, inc, dec }">
    <div>Count:{{ count }}</div>
    <div>
      <button @click="() => inc()">increase</button>
      <button @click="() => dec()">decrease</button>
    </div>
  </use-counter>
</template>

これで設定は完了です。useCounterの関数を利用した場合と動作は変わりません。

useCounterを利用したカウント
UseCounterコンポーネントを利用したカウント

カウンター機能というシンプルなコードを利用してComposablesな関数、コンポーネントを作成することができました。特にコンポーネントについてはScoped Slotsを利用するため最初は戸惑ってしまう人もいるかもしれませんがどのように値を渡し、受け取るかがわかってしまえば関数とそれほど作成の難しさに差はないかと思います。

Composition APIではComposablesな関数を利用して再利用な関数を簡単に利用できることが特徴です。VueUseに存在しない関数が必要な場合はぜひComposablesな関数を自作して効率的な開発に役立ててください。