Composition APIとは?
Composition APIとは、Vue3から導入された新機能で、コンポーネントを実装するAPIを指します。「リアクティブな値やそれに関連した関数をコンポーネントから切り離して扱える」という特徴を持つことから、コードの再利用性や保守性の向上が期待されています。
Vue2のサポートが2023年12月31日をもって終了したため、Vue2からVue3への移行を検討しているプロジェクトも増えていると思います。Vue3への移行に伴い、Composition APIもますます広まることが予測されるため、本記事が少しでもComposition API を知るとっかかりになればと思います。
Vue2とComposition API
Composition API は Vue3から導入された機能ですが、実は Vue2 でも扱う事ができます。
当記事ではVue3としてのComposition APIを紹介しているので省きますが、興味のある方は導入方法についてぜひ調べてみてください。
Options APIとの比較
Options APIは、前バージョンである Vue2 で一般的に使用されていたAPIであり、dataやmethodなどの各オプションごとにロジックを定義していく記法が特徴です。
従来の Options API の問題点として、「リアクティブな値を View から切り離せないため、ロジックも分離できない」という点がよく挙げられます。
一方、Composition API ではこれらの問題が解消され、Viewとロジックを分離することが可能です。その結果、可読性が高まりスッキリとしたコンポーネントを作成することができます。
Options APIとComposition APIのどちらを使えばいいのか?
公式の文章を噛み砕いて載せると「Composition API は大規模なプロジェクトにおいて利点を活かしやすいが、小〜中規模の開発であれば Options API でも問題ない」とのことです。
Options API が廃止される予定は今のところないようなので、個人開発でどちらを使うか迷った際には、好みで選んでみても良いと思います。
参考:https://vuejs.org/guide/extras/composition-api-faq.html
Composition APIの記述方法
script setup
Composition APIでは、通常の<script>ではなく<script setup>を使用します。 この<script setup>とは、Vue3.2で正式リリースされたComposition APIのシンタックスシュガー(糖衣構文)です。
<script>ブロックは、データやメソッドをexport defaultで明示的に指定する必要がありますが、<script setup>を使うと、これらの定義が簡潔になります。
<template>
 <div>{{ count }}</div>
 <button @click="increment">click!</button>
</template>
<script setup>
let count = 0;
const increment = () => count++;
</script>
SFC( Single File Component)について
上記サンプルコードのようにtemplate、script、styleを一つのファイルで管理しているコンポーネントのことをSFC(単一ファイルコンポーネント)と言い、Vue.js独自のフォーマットになります。しかし、このままだと変数countはDOMには反映されず、ボタンを押しても数字が増えることはありません。つまりcountはリアクティブな値とは言えないのです。変数をリアクティブにするためには次に説明するrefとreactiveを使用します。
リアクティブな値とは
変数の値を変更するとDOMへの反映操作をJSで記述しなくともDOMへ自動反映される属性を持つ変数のことを言います。
ref/reactive
ではまず、refをvueからインポートして使用してみましょう。
import { ref } from 'vue';
const count = ref(0);
const increment = () => count.value++;
refで定義した変数の値は、valueプロパティによってアクセスが可能です。そのため、const increment = () => count++;のままでは変数countに直接アクセスすることができません。しかし、<template>内ではvalueの記述は不要になります。
<template>
 <div>{{ count }}</div>
</template>
変数countはプリミティブな値でしたが、同様の方法でオブジェクトをリアクティブにすることも可能です。
import { ref } from 'vue';
const obj = ref({ a: 0, b: 0 });
const increment = () => obj.value.a++;
const reset = () => obj.value = { a: 0, b: 0 };
ですが、obj.value.aとするのは少し冗長的に思えなくもないです。 
そこで登場するのがreactiveになります。
import { reactive } from "vue";
const obj = reactive({ a: 0, b: 0 });
const increment = () => obj.a++;
const reset = () => Object.assign(obj, { a: 0, b: 0 });
reactiveであればvalueを付けずにプロパティへのアクセスが可能なので、より直感的にコードを書く事ができます。しかしreactiveの場合は直接代入が不可能なので、Object.assign()を使用する必要があります。
一般的な使い分けとして、「プリミティブな値はref、オブジェクトはreactive」という印象がありますが、refはvalueをつけなければならない一方で、一目見てリアクティブな値だと判断しやすいので、好みなどがなければ基本的にはrefを使うで問題ないかと思われます。
computed(算出プロパティ)
computedは値の変更があるたびに自動で処理を実行し、その結果を1つだけ返すというものです。Options APIではcomputedプロパティとして定義していましたが、Composition APIでは以下のように使用します。
import { ref, computed } from 'vue';
const number = ref(1);
const doubleNumber = computed(() => number.value * 2);
const displaNumber = () => {
  console.log(number.value, doubleNumber.value);
}
上記の例では、変数numberを二倍にしたdoubleNumberをcomputedで定義しています。 computedの値はrefと同様valueプロパティ経由でアクセスします。
また、computedはデフォルトでGetterとして動作しますが、Setterもサポートされており、書き込みが可能なcomputedのことをWritable Computedといいます。
<template>
  <input type="number" v-model="doubleNumber" />
</template>
<script setup>
import  { ref, computed } from 'vue'
const number = ref(1)
const doubleNumber= computed({
 get: () => number.value * 2,
 set: (val) => {
    number.value = val
  }
})
</script>
Writable Computedはv-modelを使用する際などにとても便利です。上記の例では、inputに入力された数値を自動的に2倍にして返す処理を行なっています。
props
propsを使用することで、親コンポーネントから子コンポーネントへデータを渡すことができます。親コンポーネントからデータを受け取るためには、子コンポーネント側でdefinePropsを使用します。
const props = defineProps<{
  message: string
}>();
上記の例では、messageプロパティのデータ型をstringとして指定しています。受け取った値はprops.messageのようにアクセスしますが、<template>内ではpropsは不要です。
<template>
 <div>{{ message }}</div>
</template>
続いて、親コンポーネントからpropsを渡すには、子コンポーネントを呼び出す際に以下のように設定します。
<template>
  <div>
    <ChildComponent message="Hello world!" />
  </div>
</template>
<script setup>
import ChildComponent from './ChildComponent.vue';
</script>
これにより、messageプロパティに"Hello world!"という値が渡されます。
emit
emitでは、イベントを利用して子コンポーネントから親コンポーネントへ通知を行い、イベントと一緒に親コンポーネントにデータを渡すことが可能です。親コンポーネントへイベントを通知するには、子コンポーネント側でdefineEmitsを使用します。
<template>
  <div>
    <button @click="sendMessage">Send Message!</button>
  </div>
</template>
<script setup>
import { ref } from 'vue';
const emit = defineEmits<{
  (e: 'sendMessage', message: string): void
}>();
const message = ref('Hello world!');
const sendMessage = () => {
  emit('sendMessage', message);
};
</script>
emitは2つ以上の引数を取るため、emitの第一引数にはトリガーしたいイベントの名前を、第二引数以降には親コンポーネントに渡したいデータを指定します。
また、親コンポーネントでデータを受け取る際は以下のようにします。
<template>
  <ChildComponent @sendMessage="sendMessage" />
</template>
<script setup>
import ChildComponent from './ChildComponent.vue';
const sendMessage = (message: string) => {
  console.log(message);
}
</script>
これにより、ボタンクリックでsendMessageというイベントの通知と共にmessageの値を親コンポーネントに送信できます。
ライフサイクルフック
ライフサイクルフックとは、Vueで作成したコンポーネントが生成されて、消えるまでの状態遷移のことを言い、Options APIとComposition APIで関数名が若干異なります。
| Options API (vue2) | Composition API (vue3) | 
| beforeCreate | – | 
| created | – | 
| beforeMount | onBeforeMount() | 
| mounted | onMounted() | 
| beforeUpdate | onBeforeUpdate() | 
| updated | onUpdated() | 
| beforeDestroy | onBeforeUnount() | 
| destroyed | onUnmounted() | 
Composition APIでは、<script setup>内に定義したすべての処理がbeforeCreateとcreatedに該当します。 そのため、この2つは関数が用意されていません。
ライフサイクルフックを使用する際はvueからimportして使います。
最後に
Composition APIの基本について紹介していきましたが、Composition API独自の機能はまだいくつかあります。各ライフサイクルフックの詳細な説明など、省略している部分も結構あるので、Vue3もといComposition APIに興味のある方や業務で使用している方は、ぜひ色々調べてみてください。
Vue.jsとは【入門】メリット・特徴比較でわかりやすく解説
Vue.js3とは?特徴・環境構築のやり方を解説
          