👋

Vue3のComposition APIを試してみる

2021/03/13に公開

前回、Rails + vue3 + Typescriptの導入をしました。
そのコードをComposition APIを使って書き直していきます。
前回の記事
前回までのソースコード

Composition APIとはなんぞや?という方は、こちらの記事がわかりやすかったので読んでみてください。
この記事では、既存のコードをどのようにComposition APIに置き換えたかをまとめます。

コードの書き換え

dataやmethod、watchで定義していたものは全部setupに定義します。
watchはこの記事では扱ってないので、ドキュメントをみてください。

data -> setup に変更

一番ここでつまずきました。
refとreactiveというメソッドを使って書き換えるのですが、使い分けがよくわからなかったです。

vueのドキュメントからref、reactiveの説明を抜粋すると

Vue 3.0 では、このように新しい ref 関数にとってあらゆる変数をリアクティブにすることができます

ここでリアクティブ?となりました。(この記事の最後にリアクティブの説明を載せてみたので読みたい方は読んでください)
とりあえず、objectはreactive、numberとかstringとか(プリミティブ)はrefで定義しましょうという感覚です。

実際にコードを変換すると、、、

before
  data() {
    return {
      form: {
        name: ''
      } as Form,
      users: [] as User[]
    }
  },
after
import {defineComponent, reactive} from 'vue'
// ...
  setup() {
    const form = reactive<Form>({name: ''})
    const users = reactive<User[]>([])
    return {form, users}
  }

これでtemplateの中でformやusersにアクセスできるようになります。

methods -> setup

before
  methods: {
    async createUser() {
      const user = await createUser(this.form)
      this.users.push(user)
      this.form.name = ''
    }
  },
after
import {defineComponent, reactive, ref} from 'vue'
import * as UserApi from '/@/apis/users_api' // apiの読み込み方も変更しました。
// ...
  setup() {
    const form = ref<Form>({name: ''}) // !!refになっている!!
    const users = reactive<User[]>([])
    const createUser = async () => {
      const user = await UserApi.createUser(form.value)
      users.push(user)
      form.value = {name: ''}
    }
    return {form, users, createUser}
  },

ただ単にcreateUserを移して、returnするだけでいいや。。。と思いきや、formがrefに変更しています。
なんでこうしたかというと、objectの中身を変更ではなく、object自体を変更したいからです。
formのnameだけを変更したいのであれば、reactiveのままでform.name = ''(中身の変更)にすればよかったです。
が、form = {name: ''}(object自体の変更)のように書きたかったので、reactiveからrefに変更しました。(ポインタと似てますね)

createdAt -> setup

before
  async created() {
    this.users = await getUsers()
  }

ここで問題になってくるのが、普通にsetupをasyncに置き換えても動作しないということです。
これを解決するために、suspenseという機能を使います。
suspenseは非同期処理の処理中の時(#fallback)と処理完了時(#default)、に表示するコンポーネントを切り替えることができる機能です。

<template>
  <suspense>
    <template #fallback>
      <p>処理中</p>
    </template>

    <template #default>
      <p>完了</p>
      <!-- ここにsetupがasyncのコンポーネントを配置できる -->
    </template>
  </suspense>
</template>

async用のコンポーネントを別に用意しないと行けないので、User.vueを作成して、それを<template #default>の配下でロードするようにします。
これは記事に書くと長ったらしくなるので、githubのコードを実際にみてください。
App.vue
User.vue

最後に

Composition APIに変更も意外と簡単にできました。
今度は、Vuexの導入をしたいと思います。

付録

リアクティブとは

まとめてみましたが、いまいちだったので気力がでたら書き直します。

色々調べた結果、こんな考えにまとまりました。
コードで書くとわかりやすそうなのでコードで説明します。

let i = 1
console.log(i) // 1
i + 1
console.log(i) // 1

これをみてみると当たり前じゃん!ってなると思います。これがリアクティブではないということです。
逆にリアクティブとはこんな感じです。

let obj = {}
console.log(obj) // {}
obj.message = 'hello!' 
console.log(obj) // {messag: 'hello'}

リアクティブはオブジェクトの変更に対して追随するということです。
ポインタを知っている方であれば、refはポインタ、reactiveは中身という認識でいいと思います。

Discussion