481
421

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

【Vue.js】Composition API時代の便利ライブラリ「VueUse」を使ってみた

Last updated at Posted at 2020-06-20

Vue Composition API によって Vue.js にも React Hooks のようなロジックの再利用性の高い開発体験がもたらされようとしています。
しかし、まだ「Composition API の良さをわかっていない」という方や「Composition API をうまく利用した書き方がわからない」という方も多いかと思います。

本記事では Composition API 時代の便利ライブラリ VueUse を用いた実装例や、 VueUse 自体の実装がどのようなものか紹介します。
Composition API の良さや雰囲気もキャッチアップしていただければ幸いです。

VueUse とは?

VueUseAnthony Fu さん1が中心に開発しているライブラリで、Composition API を用いた便利系関数を数多く集めたライブラリです。

例えば、ブラウザ上のマウスポインタの座標をリアクティブに取得する useMouse(), ブラウザ API の localStorage を使って状態を保持できる useLocalStorage(), 負荷対策のために連続する関数呼び出しを防ぐ useDebounceFn() 2などといった関数が提供されています。

vueuse.js.org.png

公式サイト: https://vueuse.org/
GitHub: vueuse/vueuse
npm: @vueuse/core

検証環境の構築

GitHub リポジトリの Description に記載通り、Vue 2系・3系のどちらでも利用可能です:

🧰 Collection of Composition API utils for Vue 2 and 3 https://vueuse.js.org/

今回は Vue CLI で立ち上げた Vue 2.6 系のプロジェクトに @vue/composition-api をプラグインとして追加した環境で検証します。

動作確認した環境

  • macOS Catalina
  • Chrome 83
  • Node.js 12.18.1
  • npm 6.14.5
  • Vue CLI v4.4.4
    • vue 2.6.11
  • @vue/composition-api 0.6.5
  • @vueuse/core 2.0.34

Vue CLI のインストール

npm i -g @vue/cli

Vue CLI をグローバルインストールしたくない方は、以下の手順の vue コマンド部分を npx @vue/cli に読み替えていただいても大丈夫です。

プロジェクトの作成

vue-2-vueuse-trial というプロジェクト名で環境を構築していきます:

vue create vue-2-vueuse-trial

設定は以下のようにしました:

Vue CLI v4.4.4
? Please pick a preset: Manually select features
? Check the features needed for your project: Babel, TS, Linter
? Use class-style component syntax? No
? Use Babel alongside TypeScript (required for modern mode, auto-detected polyfills, transpiling JSX)? No
? Pick a linter / formatter config: Basic
? Pick additional lint features: Lint on save
? Where do you prefer placing config for Babel, ESLint, etc.? In dedicated config files
? Save this as a preset for future projects? No

プラグインと VueUse の導入

プロジェクトディレクトリが出来上がったら、@vue/composition-api と VueUse を npm install します:

(2023/02/25 追記) Vue 2.7, Vue 3.x の場合は @vue/composition-api のインストールは不要です

cd vue-2-vueuse-trial
npm i @vueuse/core @vue/composition-api

src/main.tsVue.use(VueCompositionAPI) を追加します3:

src/main.ts
 import Vue from 'vue'
+import VueCompositionAPI from '@vue/composition-api'
 import App from './App.vue'


+Vue.use(VueCompositionAPI)
 Vue.config.productionTip = false

npm run serve で開発ビルドを開始してください。

VueUse の関数を使ってみる

useMouse()

リアクティブにマウスポインタの座標が取得できる useMouse() を使ってみます:

src/App.vue
<template>
  <div>
    {{ x }}, {{ y }}
  </div>
</template>

<script lang="ts">
import { defineComponent } from '@vue/composition-api'
import { useMouse } from '@vueuse/core'

export default defineComponent({
  setup() {
    // tracks mouse position
    const { x, y } = useMouse()

    return { x, y }
  }
})
</script>

useMouse.gif
これだけでマウスポインタの座標がリアクティブに反映されていることがわかるかと思います。

useMouse() の実装を確認すると useEventListener() という関数を使っていて、 useEventListener() はコンポーネントのマウント時(Composition API の onMounted() を利用)にイベントリスナーを追加していることがわかります:
https://github.com/antfu/vueuse/blob/master/packages/core/useMouse/index.ts
https://github.com/antfu/vueuse/blob/master/packages/core/useEventListener/index.ts

VueUse ではこのような関数がより抽象的な関数を参照しているパターンの実装が所々に見られます。
Composition API を用いた良い実装の例として知っておくと良いかと思います。

useLocalStorage()

ブラウザ API の localStorage を使って状態を保持できる useLocalStorage() を使ってみます。

まずは useLocalStorage() を使わず、 VueUse がなくても利用できる reactive() 4 を使ってみましょう:

src/App.vue
<template>
  <div>
    <div>
      name: <input v-model="state.name" />
    </div>
    <div>
      color: <input v-model="state.color" />
    </div>
    <div>{{ state }}</div>
  </div>
</template>

<script lang="ts">
import { defineComponent, reactive } from '@vue/composition-api'

export default defineComponent({
  setup() {
    const state = reactive({
      name: 'Apple',
      color: 'red',
    })

    return { state }
  }
})
</script>

name, color を変更すると、下に表示されている JSON 形式の state の表示も更新される画面が表示されます:

スクリーンショット 2020-06-20 17.33.53.png

上記の実装では name を Banana, color を yellow のように変更してページをリロードすると、元の状態(name が Apple, color が red)に戻ります。

以下のように <script lang="ts"> ブロックを変更し、reactive() の代わりに useLocalStorage() を利用するように変更してみます:

import { defineComponent } from '@vue/composition-api'
import { useLocalStorage } from '@vueuse/core'

export default defineComponent({
  setup() {
    // persist state in localStorage
    const state = useLocalStorage(
      'my-storage',
      {
        name: 'Apple',
        color: 'red',
      },
    )

    return { state }
  }
})

useLocalStorage.gif

state が localStorage に保存されるようになったので、ページをリロードしても状態が保持されるようになりました。

サンプルアプリとしてよくある ToDo リストの状態管理に useLocalStorage() を使うようにすると、手軽にデータを保存できる ToDo リストにできて楽しいかもしれません。

公式サイトが Storybook でできている件

(2023/02/25 追記) 現在は Vitepress が利用されています
https://github.com/vueuse/vueuse/pull/277

公式サイトが Storybook でできていて、各関数を即座に試せるリファレンスとなっています。

vueuse.js.org__path=_story_sensors--usemouse.png

各関数のページ下部には関数の "Source" へのリンクがあり、ソースを見てどのような実装になっているか追っていくと Composition API を用いた良い実装の勉強となるかと思います。

所感

VueUse は多くの便利関数を提供しているので、今後お世話になる可能性が高いライブラリだと思いました。
まだ試せていない関数が多くあるので、使ってみたりコードを読んだりしてみようかと思います。

  1. @vue/composition-api (Vue 2系用プラグイン)のメンテナーでもあります

  2. useDebounceFn() は Composition API に依存しない純粋な JS の関数となっています。似た名前の関数である useDebounce() は Composition API と useDebounceFn() に依存していることに気をつけてコードを読んでみると良いかと思います

  3. VueUse の名称は関数名のプレフィックスとして use を付ける慣習に由来する物であり、Vue.use() との関連性はないと思います。なお、Vue 3系からは Vue.use() は App インスタンスに対するオプションに変更される予定(rfc: 0009-global-api-change.md)のため、発話上 VueUse と Vue.use() を区別できない問題は将来的には解消されると思います

  4. Vue 2系では import { reactive } from '@vue/composition-api' による reactiveVue.observable 相当となります。 Vue 3系では import { reactive } from 'vue' のようにして import します

481
421
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
481
421

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?