【Vue.js】Composition APIについて

## Composition APIとは
Vue.jsの３系から導入された、新しいコンポーネント作成手段です。

React Hooksのように、ロジックや状態を切り出すことを可能にします。

一方、従来からあるSFCファイルにコンポーネントの見た目/状態/ロジックをまとめて記述する作成手段をOptions APIと言います。

Options APIはコードをまとめて書けるが故に、それ以上分割できない不便さがありました。

Composition APIではロジックや状態に関するコードを切り出すことができるため、Options APIの不便さを解消できます。

また、見た目に関するコードとは別々に管理されることにより、ロジックの再利用性が高まります。

とはいえComposition APIはOptions APIを代替えしたり非推奨とするわけではなく、使い分けることが可能です。

Composition APIは、コンポーネント間で状態やロジックを共有する必要がある大規模なアプリケーション開発に向いています。

## サンプル

以下のかんたんなアプリをComposition APIで作成しました。

- 名前を入力できる
- 数値の増減ができる
- これらがリアクティブに反映される

https://codesandbox.io/s/compositionapilian-xi-4s7fhd

以下のような役割です。

- `Main.vue`：名前と数値を入力するコンポーネント、他のコンポーネントを呼び出している
- `Score.vue`：数値を表示するコンポーネント
- `JudgeName.vue`：名前を表示するコンポーネント
- `use-judge.js`：値の状態、数値の増減のロジックを切り出したファイル

```vuejs:Main.vue
<template>
  <div>
    <p>審査員名を入力</p>
    <input type="text" v-model="state.judgeName" />
    <p>得点を設定</p>
    {{ state.score }}
    <button @click="increment">+</button>
    <button @click="decrement">-</button>
  </div>
  <div class="body">
    <Score />
    <JudgeName />
  </div>
</template>

<script>
import { provide } from "vue";
import Score from "./Score.vue";
import JudgeName from "./JudgeName.vue";
import { useJudge } from "../functions/use-judge.js";

export default {
  components: {
    Score: Score,
    JudgeName: JudgeName,
  },
  setup() {
    const { state, increment, decrement } = useJudge();

    provide("inputState", state);

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

<style scoped>
省略
</style>
```

```vuejs:Score.vue
<template>
  <div class="score">
    <p class="text" :class="{ low: state.score < 90 }">
      {{ state.score }}
    </p>
  </div>
</template>

<script>
import { inject } from "vue";

export default {
  setup() {
    const state = inject("inputState");
    return {
      state,
    };
  },
};
</script>

<style scoped>
省略
</style>
```

```vuejs:JudgeName.vue
<template>
  <div class="judgeName">
    <p class="text">
      {{ state.judgeName }}
    </p>
  </div>
</template>

<script>
import { inject } from "vue";

export default {
  setup() {
    const state = inject("inputState");
    return {
      state,
    };
  },
};
</script>

<style scoped>
省略
</style>
```

```js:use-judge.js
import { reactive } from "vue";

export const useJudge = () => {
  const state = reactive({
    score: 89,
    judgeName: "山田"
  });

  const increment = () => {
    if (state.score >= 100) return;
    state.score++;
  };

  const decrement = () => {
    if (state.score <= 0) return;
    state.score--;
  };

  return {
    state,
    increment,
    decrement
  };
};
```

### setup関数

Composition APIではsetup関数内にデータやメソッドなどを設定します。

設定したものをreturnし、template内で使用する、という流れになります。

### 状態とロジックの切り出し

今回は`use-judge.js`というファイルで状態とロジックを切り出しています。（state,increment,decrement）

これらは`Main.vue`で使用しています。

```vuejs:Main.vue
const { state, increment, decrement } = useJudge();
```

この記述によって、`Main.vue`内でstate,increment,decrementを使用できます。

### リアクティブなデータ

今回のアプリでは入力された名前と数値をリアクティブに反映させています。

Composition APIでリアクティブなデータを扱いたい場合、`ref`と`reactive`という方法が用意されています。

`use-judge.js`で、`reactive`を用いてscoreとjudgeNameを定義しています。

### Provide/Inject

3系特有のものではありませんが、`Provide/Inject`についても紹介します。

`use-judge.js`でstateを定義し、`Main.vue`で使用しています。

このstateを表示させるには、`Score.vue`と`JudgeName.vue`にstateを共有する必要があります。

これを実現しているのが、`Provide/Inject`です。

まずは他のコンポーネントに共有したい値をProvideに渡します。

```vuejs:Main.vue
provide("inputState", state);
```

左側がキー名、右側が共有したい値です。

続いてInjectを使用して、Provideに設定された値を取得します。

```vuejs:Score.vue
const state = inject("inputState");
```

Provideに設定されたキー名をInjuctに指定することで、値を取得できます。

## 参考

https://qiita.com/karamage/items/7721c8cac149d60d4c4a

https://qiita.com/karamage/items/4bc90f637487d3fcecf0

https://codelikes.com/vue-provide-inject/

https://zenn.dev/azukiazusa/articles/ref-vs-article