6歳娘「パパ、Vue3のComposition APIってどう使うの？」

### 今回使った技術
- Vue3
- Nuxt3（ベータ版）
- TypeScript

# ある日の我が家
娘「ねぇ、パパ！」
娘「Vue3の、**Composition API**の使い方を教えて！」

ワイ「おお、ええで〜」
ワイ「ほな、試しにこんな感じのページを作っていくで！」

![スクリーンショット 2022-07-03 11.01.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/295452/73c61ea5-341c-2f32-6530-9638421aecbe.png)

娘「わー、数をカウントできるページなんだ！」
娘「便利そう〜！」
娘「しかも、2倍の数も確認できるんだね！」

ワイ「げへへ、照れるでぇ」
ワイ「ほな、さっそく作っていくで〜」

# カウンターを作っていく
ワイ「まずは、カウンターの数値が今いくつなのか」
ワイ「その**状態管理**をせなあかんから」

```diff_vue:/pages/index.vue
<script lang="ts">
export default defineComponent({
+ data() {
+   return {
+     count: 0
+   };
+ },
})
</script>
```

ワイ「↑こうや！」

娘「なるほど〜」
娘「状態管理したい値は、`data()`っていう関数の戻り値として書くんだね！」

ワイ「せやで〜」
ワイ「そして、2倍のカウントを表示するためには・・・」

```diff_vue:/pages/index.vue
<script lang="ts">
export default defineComponent({
  data() {
    return {
      count: 0
    };
  },
+ computed: {
+   double() {
+     return this.count * 2
+   }
+ },
})
</script>
```

ワイ「↑こうや！」

娘「`count`と連動した値を表示したい場合には」
娘「`computed`の中に関数を書けばいいんだね！」

ワイ「そうや！」

娘「パパ、HTML部分は書かなくていいの？」

ワイ「おお、せやったな！」

```diff_vue:/pages/index.vue
<template>
  <h1>カウントするページ</h1>
  <p>カウント：{{ count }}</p>
  <p>カウント（2倍）：{{ double }}</p>
  <button @click="decrement">減らす</button>
  <button @click="increment">増やす</button>
</template>
```

ワイ「↑こんな感じや！」
ワイ「ここで1回、画面を見てみよか」

![スクリーンショット 2022-07-03 11.07.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/295452/9f47e784-7820-1f69-f0c4-0728b97b1e73.png)

ワイ「おお、できとるできとる」

娘「でも、なんかエラーが出てるよ？」

![スクリーンショット 2022-07-03 11.11.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/295452/cc9c9fff-c955-2461-b4ec-5d277ff651ec.png)

ワイ「おお、まだ`decrement`と`increment`の関数を書いてないからやな」
ワイ「ほな、その2つの関数を書いていくで！」

```diff_vue:/pages/index.vue
<script lang="ts">
export default defineComponent({
  data() {
    return {
      count: 0
    };
  },
  computed: {
    double() {
      return this.count * 2
    }
  },
+ methods: {
+   increment() {
+     this.count++
+   },
+   decrement() {
+     this.count--
+   },
+ },
})
</script>
```

ワイ「↑こうやな！」

娘「へぇ、関数は`methods`の中に書くんだね！」

ワイ「せやで〜」
ワイ「これで完成や！」
ワイ「動かしてみるで〜」
ワイ「ボタンをポチ、ポチと」

![スクリーンショット 2022-07-03 11.01.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/295452/ac5bd51f-e8c4-d2e0-7fb9-c53685bde71c.png)

娘「本当だ、ちゃんと数をカウントできてる〜！」
娘「これで、数を数えられないパパでも安心だね！」

ワイ「いやそこまでアホちゃうわ」

# しかし娘ちゃん、思ってたのと違う
娘「・・・あれ？」
娘「でもこれ、Vue3の書き方っていうか」
娘「Vue2の書き方にそっくりじゃない？」

ワイ「せやで」
ワイ「Vue2の時からある`Options API`の書き方やで」

娘「私が教えて欲しかったのは、`Composition API`の方だよ」

ワイ「・・・せやったな」
ワイ「でも大丈夫やで」
ワイ「`Options API`から`Composition API`に」
ワイ「シームレスに書き換えていくこともできるからな」

娘「そうなんだ」

# Composition APIに書き換えていく
ワイ「まず、状態管理したい値が書いてある`data()`の部分は」

```diff_vue:/pages/index.vue
<script setup lang="ts">
+ const count = ref(0)
</script>

<script lang="ts">
export default defineComponent({
- data() {
-   return {
-     count: 0
-   };
- },
  computed: {
    double() {
      return this.count * 2
    }
  },
  methods: {
    increment() {
      this.count++
    },
    decrement() {
      this.count--
    },
  },
})
</script>
```

ワイ「↑こうしてやるんや」

娘「へぇ〜」

```diff_vue:/pages/index.vue
<script setup lang="ts">
</script>
```

娘「↑この中に」

```diff_vue:/pages/index.vue
<script setup lang="ts">
+ const count = ref(0)
</script>
```

娘「↑こう書いてあげるんだ[^1]」

ワイ「せやで」
ワイ「ほな、動作確認してみるで」

娘「え？」
娘「こんな中途半端な状態で動くの？」

ワイ「ああ、せやで」

![スクリーンショット 2022-07-03 11.01.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/295452/061a7325-7358-cefb-3b80-6267157fdaaa.png)

ワイ「↑ほらな」

娘「ほんとだ、ちゃんと動いてる」
娘「へ〜」
娘「Options APIとComposition APIって、**併用**できるんだ」
娘「それなら、少しずつ移行できて良さそうだね！」

ワイ「せやで」
ワイ「ただ・・・」

```diff_vue:/pages/index.vue
<script setup lang="ts">
  const count = ref(0)
</script>
```

ワイ「↑この`<script setup lang="ts">`の中に書いたコードが」
ワイ「`data()`とか`computed`とか`methods`よりも先に実行されるから」
ワイ「移していく順番は考えないとアカンみたいや」

娘「なるほどね」

# computedを移行する
ワイ「次は、`computed`の中身を、`setup`の方に移していくで」

```diff_vue:/pages/index.vue
<script setup lang="ts">
  const count = ref(0)
+ const double = computed(() => count.value * 2)
</script>

<script lang="ts">
export default defineComponent({
- computed: {
-   double() {
-     return this.count * 2
-   }
- },
  methods: {
    increment() {
      this.count++
    },
    decrement() {
      this.count--
    },
  },
})
</script>
```

ワイ「↑こうやな！」

娘「へぇ〜」

```diff_vue:/pages/index.vue
<script setup lang="ts">
  const count = ref(0)
+ const double = computed(() => count.value * 2)
</script>
```

娘「`computed`っていう関数に、関数を渡してあげる感じなんだね」

ワイ「せやで〜」

# methodsを移行する
ワイ「ほな、`methods`の中身も」
ワイ「`setup`の方に移していくで〜」

```diff_vue:/pages/index.vue
<script setup lang="ts">
  const count = ref(0)
  const double = computed(() => count.value * 2)
+ const increment = () => count.value++
+ const decrement = () => count.value--
</script>

<script lang="ts">
export default defineComponent({
- methods: {
-   increment() {
-     this.count++
-   },
-   decrement() {
-     this.count--
-   },
- },
})
</script>
```

ワイ「↑こうや！」

娘「へぇ〜」
娘「`methods`の中にあった関数は、単純に関数を書けばいいんだね！」

ワイ「せや」
ワイ「これで、下の方の`<script>`タグは要らんくなるから」

```diff_vue:/pages/index.vue
<script setup lang="ts">
  const count = ref(0)
  const double = computed(() => count.value * 2)
  const increment = () => count.value++
  const decrement = () => count.value--
</script>

- <script lang="ts">
- export default defineComponent({
- })
- </script>
```

ワイ「↑こうやな」
ワイ「ほな、動作確認してみるで」

![スクリーンショット 2022-07-03 11.01.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/295452/73c61ea5-341c-2f32-6530-9638421aecbe.png)

ワイ「よっしゃ、ちゃんと動いとるわ」

娘「わーい！」

# なぜComposition APIなのか
ワイ「でも、なんで`Composition API`なんてものが登場したんやろなぁ」
ワイ「`Options API`の方が・・・」

- 状態管理したい値は`data()`に書く
- 状態と連動して表示したい値は`computed`に書く
- 関数は`methods`に書く

ワイ「↑こんな感じで、メッチャ分かりやすいと思うけどなぁ」

娘「それも分かるけど、私は**Composition API**の方が好きだよ」
娘「状態管理する値はここ、とか」
娘「関数はここ、とか」
娘「そういう分け方じゃなく」
娘「**カウンター関連の処理はここ**、っていう感じで」
娘「コードをまとめることができるじゃん？」

ワイ「ああ〜、確かに」
ワイ「状態かどうか、関数かどうか、じゃなくて」
ワイ「カウンター関連の処理かどうか・・・にフォーカスを当てて」
ワイ「コードをまとめられるなぁ」

娘「うん」
娘「**関心事の単位で**コードをまとめれるから、私は好き」

ワイ「`Options API`で書いた場合だと」
ワイ「カウンター関連の処理を修正したい場合なんかに」
ワイ「`data()`の中を直して、`computed`の中を直して、`methods`の中も・・・って感じで」
ワイ「修正箇所がバラバラで、ちょっと探しにくかったもんな」

娘「そうそう」
娘「それに、このカウンター関連のロジックを」
娘「別のページでも使いたくなったときに、すごくやりやすいらしいよ」

ワイ「へぇ〜」

娘「ちょっとやって見せるね」

# カウンター関連の処理を、別ファイルに切り出す

娘「さっき`<script setup lang="ts">`の中に書いた内容を」

```diff_typescript:/composables/count.ts
export const useCounter = () => {
  const count = ref(0)
  const double = computed(() => count.value * 2)
  const increment = () => count.value++
  const decrement = () => count.value--

  return {
    count,
    double,
    increment,
    decrement,
  }
}
```

娘「↑こう、別ファイルに切り出すの」

ワイ「ほうほう」

娘「そして、ページの方から・・・」

```diff_vue:/pages/index.vue
<script setup lang="ts">
import { useCounter } from '~~/composables/count'

const { count, double, increment, decrement } =  useCounter()
</script>
```

娘「↑こう呼び出して使ってあげるの」

ワイ「ほうほう」

娘「こうしてあげることで、同じロジックを」
娘「色んなページで使い回すこともできるよ！」

ワイ「おぉ〜、ホンマやね」
ワイ「Options APIの場合だと、その辺ちょっとやりづらかったもんなぁ」
ワイ「Mixinとか、あるにはあったけど」

娘「そうだね」
娘「つまり・・・」

- ページやコンポーネントの中から、状態関連のロジックを抽出できる
- いろんな場所で再利用できる

娘「↑こういうことが、Composition APIのおかげでやりやすくなったよね！」

ワイ「確かに・・・！」

# まとめ
- Options APIの場合
    - 「状態かどうか」「関数かどうか」でコードの場所が決まる
- Composition APIの場合
    - **関心事の単位で**コードをまとめられる
    - 状態関連のロジックを抽出して、再利用できる
- Vue3でもOptions APIは使える
- Options APIとComposition APIは、1ファイルの中でも併用可能
- Options APIからComposition APIへ、動作確認しながら段階的に移行することも可能

ワイ「↑こういうことやな！」

娘「そうだね！」

**〜おしまい〜**

# 参考文献
- [Composition API | Vue.js](https://v3.ja.vuejs.org/api/composition-api.html)
- [Quick Start - Nuxt 3](https://v3.nuxtjs.org/getting-started/quick-start/)
- [Vue 2.xのOptions APIからVue 3.0のComposition APIへの移行で知っておくと便利なTips - ZOZO TECH BLOG](https://techblog.zozo.com/entry/vue-options-api-to-composition-api)

# こちらの記事もよろしくやで！
- [6歳娘「パパ、静的生成は色んな場面で使えるんだよ？」](https://qiita.com/Yametaro/items/3363b1f996714af1b451)

[^1]: `ref()`じゃなくて`reactive()`ってやつもあります。