カテゴリー: FrontEnd

Vue.js 3.0のComposition APIを使ってみた

はじめに

開発が進められている Vue.js 3.0 にて追加される、 Composition API を使った書き方を紹介します。

現在、 Vue.js 3.0.0 beta がリリースされており、こちらを使って実装してきます。

Composition APIとは

はじめに、 Composition API の概要について簡単に紹介します。

こちらに書いてあるとおり、 Composition API とは、コンポーネントロジックの柔軟な構成を可能にする、関数ベースのAPIです。

詳しくは、紹介するコードを見てほしいのですが、 setup 関数の引数に context を渡すことで、emit や $router や $store などに関する処理を外部に切り出すことができます。

環境構築

プロジェクトの作成は、 Vue CLI で行ってください。

プロジェクトを作成したら、プロジェクトのルートディレクトリで以下のコマンドを実行し、 Vue.js 3.0.0 beta を使用できるようにします。

$ vue add vue-next

Composition API での書き方

それでは、実際に Composition API での書き方を紹介します。

まず、 Composition API を使って書かれた以下のコードを見てください。

<template>
  <p>Count is {{state.count}}</p>
</template>

<script>
  import { reactive } from 'vue'

  export default {
    setup() {
      const state = reactive({
        count: 0
      })
      return {
        state
      }
    }
  }
</script>

このコードは、画面に「 Count is 0 」と表示するだけのシンプルなコードです。

注目すべき点は、見慣れない setup 関数と reactive 関数です。 Composition API では、リアクティブな値は reactive 関数の引数として渡し、それを setup 関数内で return する Object に含める必要があります。

ここで言う count は、従来の書き方における data に相当します。

他にも、 function 、 watch 、 lifecycle hooks なども全て setup 関数内に定義します。

function

先程のコードにボタンをクリックした時に count の値を1ずつ増加させる処理を追加したコードがこちらです。

<template>
  <p>Count is {{state.count}}</p>
  <!-- 追加 -->
  <button @click="increment">
    increment
  </button>
</template>

<script>
  import { reactive } from 'vue'

  export default {
    setup() {
      const state = reactive({
        count: 0
      })

      // 追加
      function increment() {
        state.count++
      }

      return {
        state,
        increment // 追加
      }
    }
  }
</script>

コードを見れば分かる通り、 Composition API では function を methods に含める必要がなくなり、代わりに setup 関数の return で返却する Object に含める必要があります。

computed

先程のコードに、 count の2倍の値を返却する computed である double を追加したコードがこちらです。

<template>
  <p>Count is {{state.count}}</p>
  <!-- 追加 -->
  <p>Double count is {{state.double}}</p>
  <button @click="increment">
    increment
  </button>
</template>

<script>
  import { reactive, computed } from 'vue' // 追加

  export default {
    setup() {
      const state = reactive({
        count: 0,
        double: computed(() => state.count * 2) // 追加
      })

      function increment() {
        state.count++
      }

      return {
        state,
        increment
      }
    }
  }
</script>

computed も data と同様に reactive 関数の引数に追加し、 setup 関数で return する object に含めます。

watch

count が3の倍数の時に hoge を、それ以外のときには fuga と表示させるようにします。

<template>
  <p>Count is {{state.count}}</p>
  <p>Double count is {{state.double}}</p>
  <!-- 追加 -->
  <p>{{state.message}}</p>
  <button @click="increment">
    increment
  </button>
</template>

<script>
  import { reactive, computed, watch } from 'vue' // 追加

  export default {
    setup() {
      const state = reactive({
        count: 0,
        message: "", // 追加
        double: computed(() => state.count * 2)
      })

      function increment() {
        state.count++
      }

      // 追加
      watch(() => {
        if (state.count % 3 === 0) {
          state.message = "hoge"
        } else {
          state.message = "fuga"
        }
      })

      return {
        state,
        increment
      }
    }
  }
</script>

watch は setup 関数の中で watch 関数の引数に渡す関数として定義します。 watch を複数個定義したい場合は、 watch 関数を必要な数だけ書けば良いだけです。

lifecycle hooks

mounted 内で count に初期値を設定するようにしたコードがこちらです。

<template>
  <p>Count is {{state.count}}</p>
  <p>Double count is {{state.double}}</p>
  <p>{{state.message}}</p>
  <button @click="increment">
    increment
  </button>
</template>

<script>
  import { reactive, computed, watch, onMounted } from 'vue' // 追加

  export default {
    setup() {
      const state = reactive({
        count: 0,
        message: "",
        double: computed(() => state.count * 2)
      })

      function increment() {
        state.count++
      }

      watch(() => {
        if (state.count % 3 === 0) {
          state.message = "hoge"
        } else {
          state.message = "fuga"
        }
      })

      // 追加
      onMounted(() => {
        state.count = 1
      })

      return {
        state,
        increment
      }
    }
  }
</script>

lifecycle hooks は onMounted のように onXXX の形式で提供されてるものを import して定義します。

ただし、 created と beforeCreate に関しては、 setup に置き換わっています。

その他の lifecycle hooks についてはこちらを御覧ください。

ref

ref を使うと、 state.count のように Object にまとめる必要がなくなり、 template タグ内でも直接参照することができます。

値を更新したい場合は、以下のように .value を付ける必要があります。

<template>
  <p>Count is {{state.count}}</p>
  <!-- 追加 -->
  <p>Count is {{countRef}}</p>
  <p>Double count is {{state.double}}</p>
  <p>{{state.message}}</p>
  <button @click="increment">
    increment
  </button>
</template>

<script>
  import { reactive, computed, watch, onMounted, ref } from 'vue' // 追加

  export default {
    setup() {
      const state = reactive({
        count: 0,
        message: "",
        double: computed(() => state.count * 2)
      })

      const countRef = ref(0) // 追加

      function increment() {
        state.count++
        countRef.value++ // 追加
      }

      watch(() => {
        if (state.count % 3 === 0) {
          state.message = "hoge"
        } else {
          state.message = "fuga"
        }
      })

      onMounted(() => {
        state.count = 1
      })

      return {
        state,
        increment,
        countRef // 追加
      }
    }
  }
</script>

props と emit

カウントを加算するボタンを別のコンポーネントにしたコードがこちらです。

<template>
  <p>Count is {{state.count}}</p>
  <p>Count is {{countRef}}</p>
  <p>Double count is {{state.double}}</p>
  <p>{{state.message}}</p>
  <!-- 追加 -->
  <IncrementButton :count="state.count" @emit-increment="setCount"/>
</template>

<script>
  import { reactive, computed, watch, onMounted, ref } from 'vue'
  import IncrementButton from "./IncrementButton.vue"

  export default {
    components:{IncrementButton},
    setup() {
      const state = reactive({
        count: 0,
        message: "",
        double: computed(() => state.count * 2)
      })

      const countRef = ref(0)

      // 追加
      function setCount(num) {
        state.count = num
      }

      watch(() => {
        if (state.count % 3 === 0) {
          state.message = "hoge"
        } else {
          state.message = "fuga"
        }
      })

      onMounted(() => {
        state.count = 1
      })

      return {
        state,
        countRef,
        setCount  // 追加
      }
    }
  }
</script>
<template>
  <button @click="increment">
    increment
  </button>
</template>

<script>
  import { reactive } from 'vue'
  export default {
    props: {
      count: {
        type: Number,
        default: 0
      }
    },
    setup(props, context) {
      const state = reactive({
        num:props.count
      })

      function increment() {
        state.num++
        context.emit("emit-increment", state.num)
      }

      return {
        state,
        increment
      }
    }
  }
</script>

親コンポーネントから子コンポーネントへ count が渡り、子コンポーネントから emit-increment が emit されます。

props は setup 関数の引数に props として渡し、props.count のようにアクセスします。

emit は setup 関数の引数に context を渡し、 context.emit の引数として実行します。

さいごに

今回は Composition API と TypeScript を組み合わせるところまでは調べきれなかったのですが、型推論が改善されるようなので、7月に予定されている RC版 がリリースされたら再度調べてみたいと思います。

おすすめ書籍

   

Hiroki Ono

シェア
執筆者:
Hiroki Ono
タグ: JavaScriptVuejs

最近の投稿

フロントエンドで動画デコレーション&レンダリング

はじめに 今回は、以下のように…

2週間 前

Goのクエリビルダー goqu を使ってみる

はじめに 最近携わっているとあ…

4週間 前

【Xcode15】プライバシーマニフェスト対応に備えて

はじめに こんにちは、suzu…

2か月 前

FSMを使った状態管理をGoで実装する

はじめに 一般的なアプリケーシ…

3か月 前