VueでFizzBuzzを書いてみた
あまり知られていないが、Vueの単一ファイルコンポーネントは自身を再帰的に呼び出すことができる。 この仕組みを利用してFizzBuzzを書いてみた。
コード
ファイル名をFizzBuzz.vueとして、以下のコードを書いた。
テンプレート内で自身を参照する際のコンポーネント名はファイル名と同じになる。 この例ではファイル名がFizzBuzz.vueなので、呼び出すコンポーネントの名前はFizzBuzzになる。
<script setup lang="ts">
type Props = Partial<{
i: number
n: number
}>
const { i = 1, n = 100 } = defineProps<Props>()
const value = computed(() => {
if (i % 15 === 0) return 'FizzBuzz'
if (i % 3 === 0) return 'Fizz'
if (i % 5 === 0) return 'Buzz'
return i.toString()
})
</script>
<template>
<p v-bind="$attrs">{{ value }}</p>
<!-- ここで再帰 -->
<FizzBuzz v-if="i < n" v-bind="$attrs" :i="i + 1" :n="n" />
</template>
省略するが、このコンポーネントを配置するとFizzBuzzが出力される。
Tips
コード例では次にあげるちょっとしたテクニックを利用している。
複数ルート要素
かつてVueコンポーネントは単一のルート要素を持つ必要があった(無用な
複数ルート要素を持つコンポーネントではコンポーネントに指定した属性値の自動継承が無効になる。 例として、このFizzBuzzのテキストをオシャレな色に変更するため、
<template>
<FizzBuzz class="text-fashionable-color" />
</template>
単一ルート要素のコンポーネントの場合は、そのルート要素にコンポーネントの呼び出し側で指定した属性がそのまま設定される。 この機能はAttribute Inheritanceと呼ばれている。
一方で、複数のルート要素を持つコンポーネントではどの要素に
Propsの分割代入
こちらは実験的機能だが、Vue3.3からPropsの分割代入ができるようになった。
Propsを分割代入するとリアクティブ性が失われてしまう問題があったが、このアップデートにより解決された。 いちいち
Vue3.3時点では、Propsの分割代入を利用するためには明示的な機能の有効化が必要となっている。 詳しい設定方法については上述のリンクとRFCを参照してほしい。
Nuxtでは、nuxt.config.tsに以下のように記述すればOK。
export default defineNuxtConfig({
vite: {
vue: { script: { propsDestructure: true } },
},
// ......
})
再帰的Vueコンポーネントの使い所
ネストしたUIを表現するのに使えるかもしれないと思ったが、ネストした表現を実際に使う機会はあまりなさそう。 普通のループ(
再帰が使えるという事実と、ループでは都合の悪い何かを踏んだときに再帰が使えるかもということは覚えておくと役に立つかもしれない。