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とは?特徴・環境構築のやり方を解説