🉐

カスタマイズ性抜群!Vue.jsのSlotを使ったコンポーネントの再利用テクニック

2023/07/31に公開

Vueの<slot>とは

簡単に言うと、

親コンポーネントから子コンポーネント内部のDOMを指定するための仕組み。

なぜ必要なのか

コンポーネントの再利用性を上げるため。
汎用性の高いコンポーネントを作れるとそのあとの開発がラクになる。

<slot>なしの実装

送信ボタンを作ってみます。
ボタンを押した後、ボタン内のテキストを「送信中...」に変化させて背景色もグレーにします。
(スタイルはTailwind CSSを利用しています)

送信ボタン(デフォルト)

<script lang="ts" setup></script>
<template>
    <button class="border-2 p-2 rounded-full">送信する</button>
</template>

送信ボタン(送信中)

<script lang="ts" setup></script>
<template>
    <button class="border-2 p-2 rounded-full bg-slate-200">送信中...</button>
</template>

ボタンを利用する親コンポーネント

<script lang="ts" setup>
import {ref} from 'vue';
import SubmitButton from './SubmitButton.vue';
import SendingButton from './SendingButton.vue';

const isSending = ref(false);
const send = () => (isSending.value = true);
</script>
<template>
    <div class="p-10">
        <!-- フラグによってボタン自体を切り替え -->
        <SubmitButton v-if="!isSending" @click="send"></SubmitButton>
        <SendingButton v-else></SendingButton>
    </div>
</template>

表示は「送信中...」ですが実際のロジックはisSendingの値をfalseからtrueに変えただけです。

この実装の嬉しくないところ

ほぼ同じスタイルで同じDOM構造なのに別々の.vueファイルで管理しなければいけない所が辛いです。例えば、このボタンのX軸のパディングを増やすときに2つのファイルを編集しなければいけません。

<slot>ありの実装

<slot>を使ってみます。

送信ボタンのコンポーネントが1つで済むようにしてみます。

<script lang="ts" setup></script>
<template>
    <button class="border-2 p-2 rounded-full disabled:bg-slate-200">
        <!-- 親で書いた内容がslotと置き換わる -->
        <slot />
    </button>
</template>

...以上です。ファイルも1つになってスッキリしました。

クラスのdisabled:bg-slate-200について。
属性の継承によって親からdisabled属性を受け取ることができます。

「送信する」と「送信中...」のテキストは親から指定します。

<script lang="ts" setup>
import {ref} from 'vue';
import SubmitButton from './SubmitButton.vue';

const isSending = ref(false);
const send = () => (isSending.value = true);
</script>
<template>
    <div class="p-10">
        <!-- ボタンの状態だけを切り替え -->
        <SubmitButton @click="send" :disabled="isSending">
            {{ isSending ? '送信中...' : '送信する' }}
        </SubmitButton>
    </div>
</template>

もちろん動作します。

まとめ

slotは汎用的なコンポーネントを作る上で必須な要素です。ぜひ活用してみて下さい!
この記事ではslotっていうのがあるんだよー!くらいの内容でしたが別の記事で具体的な使用方法についても紹介する予定です。

Discussion