今回使った技術
- Vue3
- Nuxt3(ベータ版)
- TypeScript
ある日の我が家
娘「ねぇ、パパ!」
娘「Vue3の、Composition APIの使い方を教えて!」
ワイ「おお、ええで〜」
ワイ「ほな、試しにこんな感じのページを作っていくで!」
娘「わー、数をカウントできるページなんだ!」
娘「便利そう〜!」
娘「しかも、2倍の数も確認できるんだね!」
ワイ「げへへ、照れるでぇ」
ワイ「ほな、さっそく作っていくで〜」
カウンターを作っていく
ワイ「まずは、カウンターの数値が今いくつなのか」
ワイ「その状態管理をせなあかんから」
<script lang="ts">
export default defineComponent({
+ data() {
+ return {
+ count: 0
+ };
+ },
})
</script>
ワイ「↑こうや!」
娘「なるほど〜」
娘「状態管理したい値は、data()
っていう関数の戻り値として書くんだね!」
ワイ「せやで〜」
ワイ「そして、2倍のカウントを表示するためには・・・」
<script lang="ts">
export default defineComponent({
data() {
return {
count: 0
};
},
+ computed: {
+ double() {
+ return this.count * 2
+ }
+ },
})
</script>
ワイ「↑こうや!」
娘「count
と連動した値を表示したい場合には」
娘「computed
の中に関数を書けばいいんだね!」
ワイ「そうや!」
娘「パパ、HTML部分は書かなくていいの?」
ワイ「おお、せやったな!」
<template>
<h1>カウントするページ</h1>
<p>カウント:{{ count }}</p>
<p>カウント(2倍):{{ double }}</p>
<button @click="decrement">減らす</button>
<button @click="increment">増やす</button>
</template>
ワイ「↑こんな感じや!」
ワイ「ここで1回、画面を見てみよか」
ワイ「おお、できとるできとる」
娘「でも、なんかエラーが出てるよ?」
ワイ「おお、まだdecrement
とincrement
の関数を書いてないからやな」
ワイ「ほな、その2つの関数を書いていくで!」
<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
の中に書くんだね!」
ワイ「せやで〜」
ワイ「これで完成や!」
ワイ「動かしてみるで〜」
ワイ「ボタンをポチ、ポチと」
娘「本当だ、ちゃんと数をカウントできてる〜!」
娘「これで、数を数えられないパパでも安心だね!」
ワイ「いやそこまでアホちゃうわ」
しかし娘ちゃん、思ってたのと違う
娘「・・・あれ?」
娘「でもこれ、Vue3の書き方っていうか」
娘「Vue2の書き方にそっくりじゃない?」
ワイ「せやで」
ワイ「Vue2の時からあるOptions API
の書き方やで」
娘「私が教えて欲しかったのは、Composition API
の方だよ」
ワイ「・・・せやったな」
ワイ「でも大丈夫やで」
ワイ「Options API
からComposition API
に」
ワイ「シームレスに書き換えていくこともできるからな」
娘「そうなんだ」
Composition APIに書き換えていく
ワイ「まず、状態管理したい値が書いてあるdata()
の部分は」
<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>
ワイ「↑こうしてやるんや」
娘「へぇ〜」
<script setup lang="ts">
</script>
娘「↑この中に」
<script setup lang="ts">
+ const count = ref(0)
</script>
娘「↑こう書いてあげるんだ1」
ワイ「せやで」
ワイ「ほな、動作確認してみるで」
娘「え?」
娘「こんな中途半端な状態で動くの?」
ワイ「ああ、せやで」
ワイ「↑ほらな」
娘「ほんとだ、ちゃんと動いてる」
娘「へ〜」
娘「Options APIとComposition APIって、併用できるんだ」
娘「それなら、少しずつ移行できて良さそうだね!」
ワイ「せやで」
ワイ「ただ・・・」
<script setup lang="ts">
const count = ref(0)
</script>
ワイ「↑この<script setup lang="ts">
の中に書いたコードが」
ワイ「data()
とかcomputed
とかmethods
よりも先に実行されるから」
ワイ「移していく順番は考えないとアカンみたいや」
娘「なるほどね」
computedを移行する
ワイ「次は、computed
の中身を、setup
の方に移していくで」
<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>
ワイ「↑こうやな!」
娘「へぇ〜」
<script setup lang="ts">
const count = ref(0)
+ const double = computed(() => count.value * 2)
</script>
娘「computed
っていう関数に、関数を渡してあげる感じなんだね」
ワイ「せやで〜」
methodsを移行する
ワイ「ほな、methods
の中身も」
ワイ「setup
の方に移していくで〜」
<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>
タグは要らんくなるから」
<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>
ワイ「↑こうやな」
ワイ「ほな、動作確認してみるで」
ワイ「よっしゃ、ちゃんと動いとるわ」
娘「わーい!」
なぜComposition APIなのか
ワイ「でも、なんでComposition API
なんてものが登場したんやろなぁ」
ワイ「Options API
の方が・・・」
- 状態管理したい値は
data()
に書く - 状態と連動して表示したい値は
computed
に書く - 関数は
methods
に書く
ワイ「↑こんな感じで、メッチャ分かりやすいと思うけどなぁ」
娘「それも分かるけど、私はComposition APIの方が好きだよ」
娘「状態管理する値はここ、とか」
娘「関数はここ、とか」
娘「そういう分け方じゃなく」
娘「カウンター関連の処理はここ、っていう感じで」
娘「コードをまとめることができるじゃん?」
ワイ「ああ〜、確かに」
ワイ「状態かどうか、関数かどうか、じゃなくて」
ワイ「カウンター関連の処理かどうか・・・にフォーカスを当てて」
ワイ「コードをまとめられるなぁ」
娘「うん」
娘「関心事の単位でコードをまとめれるから、私は好き」
ワイ「Options API
で書いた場合だと」
ワイ「カウンター関連の処理を修正したい場合なんかに」
ワイ「data()
の中を直して、computed
の中を直して、methods
の中も・・・って感じで」
ワイ「修正箇所がバラバラで、ちょっと探しにくかったもんな」
娘「そうそう」
娘「それに、このカウンター関連のロジックを」
娘「別のページでも使いたくなったときに、すごくやりやすいらしいよ」
ワイ「へぇ〜」
娘「ちょっとやって見せるね」
カウンター関連の処理を、別ファイルに切り出す
娘「さっき<script setup lang="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,
}
}
娘「↑こう、別ファイルに切り出すの」
ワイ「ほうほう」
娘「そして、ページの方から・・・」
<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
- Quick Start - Nuxt 3
- Vue 2.xのOptions APIからVue 3.0のComposition APIへの移行で知っておくと便利なTips - ZOZO TECH BLOG
こちらの記事もよろしくやで!
-
ref()
じゃなくてreactive()
ってやつもあります。 ↩