シナプス技術者ブログ

シナプスの技術者公式ブログ。インターネットで、鹿児島の毎日を笑顔にします。

Vueのコンポーザブル関数とVueUseについて

こんにちは、技術部システム開発課の今門です。
Vue/Nuxtでの開発において、コンポーザブル関数を利用する機会が増えています。
今回は、Vueのコンポーザブル関数について説明します。 また併せて、Composition APIを用いて作られたコンポーザブルな関数をまとめたライブラリである、VueUseについても紹介できればと思います。

コンポーザブル関数が使われる背景

Option APIの特徴と問題点

Vueでは、従来よりコンポーネントを構築する方法としてOptions APIを採用していました。
これは、コンポーネントのオプション (data, computed,methods, watchなど) ごとにロジックをまとめて書く記述方法です。
その記述⽅法は⾒通しがよく、そのわかりやすさが、Vue.jsの⼈気の原因でもありました。
しかし、次のような問題点がありました。

  • 処理ごとのデータやコードが分散してしまいがち。
  • そうなると、処理ごとにデータやコードを共通化して、外部から呼び出すという再利⽤化が難しくなる。
  • それゆえ、規模の⼤きい開発では逆に見通しが悪くなる。

コードとしては、次のようになります。

// Options APIの例。処理毎にデータや関数がまとまっておらず、オプション間で区切られている。

const app = Vue.createApp({});

app.component('my-comp', {
  data() {
    return {
      variable1: 0,  //処理1で使うデータ
      variable2: 0,  //処理2で使うデータ
      variable3: 0  //処理3で使うデータ
    }
  },
  method: {
      func1() {・・・},   //処理1で使う関数
      func2() {・・・},  //処理2で使う関数
      func3() {・・・}   //処理3で使う関数
  },
  template: `
    <div>・・・</div>`
});
app.mount('#app');

Composition API による問題点の解決

その解決手段として、Vue3より Composition APIが新たに採用されました。
実は、このComposition API、Vue2の頃からプラグインとしては提供されていたのですが、Vue3へのバージョンアップに伴い、標準機能として搭載されたという流れになります。
従来からのOptions APIももちろん利用できるのですが、このComposition APIがデフォルトの位置付けとなっています。

Composition APIの特徴は、次のとおりです。

  • コンポーネントのソースコードを、setupオプションあるいはsetupメソッドに押し込めることができる。
  • 関連する処理ごとにデータやソースコードがまとまっているため、共通化、再利用が行いやすくなる。
  • 大規模開発も行いやすい。

コードとしては、次のように改善されます。

// Conposition APIの例。処理毎にデータや関数がまとまっている。

Vue
.createApp({})
.component('my-comp', {

    props: [ 'msg1', 'msg2', 'msg3' ],
    template: `<div>・・・</div>`

    setup(props) {

// 処理1に関連するデータや関数などを記述。このまとまりを共通化/外部ファイル化。
  const variable1 = Vue.ref( props.msg1 )
  const func1 = function() {・・・}
  const compd1 = Vue.computed(function() {・・・})

// 処理2に関連するデータや関数などを記述。このまとまりを共通化/外部ファイル化。
  const variable2 = Vue.ref( props.msg2 )
  const func2 = function() {・・・}
  const compd2 = Vue.computed(function() {・・・})

// 処理3に関連するデータや関数などを記述。このまとまりを共通化/外部ファイル化。
  const variable3 = Vue.ref( props.msg3 )
  const func3 = function() {・・・}
  const compd3 = Vue.computed(function() {・・・})

    }

    return {

        variable1, func1, compd1,
        variable2, func2, compd2,
        variable3, func3, compd3

    }


})
.mount('#app');

Composition APIの標準採用によって、処理を関数としてまとめやすくなりました。

コンポーザブル関数

コンポーザブル関数とは、Composition API を活用して状態を持つロジックをカプセル化して再利用するための関数のことです。

コンポーザブル関数の例

例えば日付をフォーマットするような関数は、状態のないロジックをカプセル化していると言えます。
それに対して、状態を持つロジックのカプセル化について、Vueの公式サイトでは、マウストラッカーを実現するコンポーネントを例としてあげています。

コンポーザブル関数 useMouse により、マウスの座標を出力するコンポーネントは、次のとおりです。

<script setup>
import { useMouse } from './mouse.js'

const { x, y } = useMouse()
</script>

<template>Mouse position is at: {{ x }}, {{ y }}</template>

コンポーザブル関数 useMouseでは、イベントの発生と同時に、x座標とy座標の位置を取得しています。

// mouse.js
import { ref, onMounted, onUnmounted } from 'vue'

export function useMouse() {
  // コンポーザブルによってカプセル化および管理される状態
  const x = ref(0)
  const y = ref(0)

  function update(event) {
    x.value = event.pageX
    y.value = event.pageY
  }

  // コンポーザブルは所有コンポーネントのライフライクルにフックして
  // 副作用のセットアップや破棄することもできます。
  onMounted(() => window.addEventListener('mousemove', update))
  onUnmounted(() => window.removeEventListener('mousemove', update))

  // 管理された状態を戻り値として公開
  return { x, y }
}

公式サイトでは下記のとおり、上記コンポーネントのPlaygroudが公開されています。

play.vuejs.org

慣例・制限事項など

コンポーザブル関数を使用するためには、下記のルールに留意する必要があります。

  • "use" で始まるキャメルケースの名前をつける。
  • 入れ子にすることも可能である。
  • リアクティビティーに依存しない場合でも ref や getter の引数を受け取れる。
  • 常に複数の ref を含むプレーンでリアクティブでないオブジェクトを返したほうがよい。そうすることにより、コンポーネントでリアクティビティーを保ったまま分割代入できる。
  • SSRの場合、マウント後のライフサイクルフック内でDOM イベントリスナーの登録やデータの取得などを実行するようにする。そうすることで、DOM にアクセスできることが保証される。また、イベントリスナーを削除することすることも忘れないようにする。
  • <script setup>か "setup()" フックの中で呼び出す。
  • コンテキスト内で同期的に呼び出す。

VueUseについて

VueUseとは

VueUseは、リファクタリングやコードの再利用を容易にするためのカスタムコンポジション関数を提供するライブラリです。
これにより、コンポーネントのロジックをより小さな部分に分割して、再利用しやすくなります。

VueUseの特徴には、次のようなものがあります。

  • Composition APIにより構成される。
  • TypeScriptでの記述が可能。
  • Vue2、Vue3でも利用できる。
  • CDNを介して利用できる。
  • オープンソースプロジェクトであり、コミュニティにより成り立っている。

VueUseの機能の範囲

このような小規模なライブラリ群は、ユーティリティ的な機能を提供している印象があります。
しかし、UI構築やアニメーション、センサーに関する機能も提供されています。
現時点(2024年2月)で、公式サイトで紹介さいれているカテゴリは、次のとおりです。

オープンソースのコミュニティーが活発であることから、今後も増えていくことが想定されます。

Nuxt3でのVueUseの始め方

VueUseを利用する環境としては、現在はNuxt3が最も多いと多いと思います。
VueUse 7.2.0からは、自動インポートを可能にする Nuxt モジュールが提供されています。

npm i -D @vueuse/nuxt @vueuse/core

また、nuxt.config.tsに次のとおり設定します。

export default defineNuxtConfig({
  modules: [
    '@vueuse/nuxt',
  ],
})

VueUseの使用例

一例として、useToggleというコンポーザブル関数を紹介します。

公式サイトに実際の導入例が掲載されています。
このような処理は、現在の状態を判定し、イベント発生に応じて別の状態に切り替える/あるいは条件によっては切り替えないというちょっと面倒なロジックが発生します。

usieToggleを使うと、次のようなシンプルな記述で実現できます。

<script setup lang="ts">
//Nuxt3かつVueUse 7.2.0の場合は、自動インポートも可能
import { useToggle } from '@vueuse/core'

const [value, toggle] = useToggle()
</script>

<template>
  <div>
    <p>Value: {{ value ? 'ON' : 'OFF' }}</p>
    <button @click="toggle()">
      Toggle
    </button>
    <button @click="value = true">
      Set ON
    </button>
    <button @click="value = false">
      Set OFF
    </button>
  </div>
</template>

まとめ

コンポーザブル関数、そしてさらにはVueUseのようなライブラリは、開発スピードをあげるためにも使っていきたいものです。

ただし、特にライブラリに関しては、どれくらい学習コストがかかるのか、今後も開発が継続され、メンテナンスも頻繁に行われるのかを見極めながら、導入可否を判断したいと思います。

参考サイト

ja.vuejs.org

nuxt.com

vueuse.org