Vue.jsのSlot(スロット)の使い方!(子コンポーネントにコンテンツを渡す)

Vue.js Vue.js

Vue.jsのSlotの使い方について書いています。
サンプルコードはVue.jsのバージョン3で検証しています。

Vue.jsのSlotとは?

Vue.jsのSlotは子コンポーネントに、使う側の親コンポーネントからコンテンツを渡せるという機能です。
実際にSlotを使用する例を書きます。今回はTailwindCSSを使用していい感じにスタイルをつけています。

下記のコンポーネントを作成しました。(名前はmy-cardにしました)
カード形式で情報を表示するコンポーネントです。Slotでコンテンツを受け取れるようにしています。

<template>
  <div class="max-w-sm rounded overflow-hidden shadow-lg">
    <div class="px-6 py-4">
      <div class="font-bold text-xl mb-2">テストカード</div>
      <p class="text-gray-700 text-base">
        <slot />
      </p>
    </div>
  </div>
</template>

上記のコンポーネントで<slot />としている箇所に親から渡されたコンテンツが展開されます。

親から、このカードを使用するためには下記のように使います。

<template>
  <my-card class="m-5">
    カードのコンテンツ
  </my-card>
</template>

作成したmy-cardコンポーネントのタグの間に、「カードのコンテンツ」という文字列を渡しています。
これがコンポーネントの<slot />に展開される内容になります。

実際にブラウザで作成したカードを表示してみると、下記のように渡された文字列が展開されて表示されました。

Vue.jsでSlotの機能をテスト

今回は文字列のみを渡しましたが、下記のようにHTMLを渡すことも可能です。

<template>
  <my-card class="m-5">
    カードのコンテンツ<br />
    <button class="rounded border-blue-800 bg-blue-300 border-2 px-3 py-1">
      ボタン
    </button>
  </my-card>
</template>

buttonタグを使用して、ボタンを作ってみました。
このように渡すと、下記のように<slot />箇所に渡したコンテンツが展開されることが確認できます。

Vue.jsでSlotにHTMLを渡してみる

Propsとは違うのか?

似たような機能として、PropsがありますがPropsは親コンポーネントから文字列や配列などの値を渡すための機能になります。
Slotは、先ほどのようにHTMLコンテンツを渡すための機能になります。

名前付きSlot(スロット)の使い方

先ほどはSlotに渡したコンテンツがひとつでしたが、名前付きSlotを使用することで、コンテンツを複数渡すことが可能です。
上記で作成したmy-cardコンポーネントにもうひとつSlotを実装してみます。

<template>
  <div class="max-w-sm rounded overflow-hidden shadow-lg">
    <div class="px-6 py-4">
      <div class="font-bold text-xl mb-2"><slot name="title" /></div>
      <p class="text-gray-700 text-base">
        <slot name="content" />
      </p>
    </div>
  </div>
</template>

カードのタイトルの箇所もSlotで渡すようにしてみました。
<slot name="title" />がタイトルを受け取るためのSlotになります。
<slot name="content" />がコンテンツを受け取るためのSlotです。

これで、タイトルと内容がSlotを使用して親コンポーネントから渡せるようになります。
実際に親コンポーネントから渡す場合は、下記のように使用します。

<template>
  <my-card class="m-5">
    <template v-slot:title>食べ物カード</template>
    <template v-slot:content>
        <ul>
          <li>ラーメン</li>
          <li>ハンバーガー</li>
          <li>ピザ</li>
        </ul>
    </template>
  </my-card>
</template>

templateタグを使用して、それぞれのSlotにコンテンツを渡しています。
templateタグ自体は表示されることはありません。

v-slot:titleと書いてる箇所に渡している「食べ物カード」が子コンポーネントの<slot name="title" />に反映されます。
v-slot:contentと書いている箇所に渡しているリストが子コンポーネントの<slot name="content" />に反映されます。

実際にブラウザで確認すると、下記のように渡した内容が反映されることが確認できます。
Vue.jsの名前付きスロットを試してみる

名前付きSlotを使用しましたが、先ほどのようにSlotがひとつだけ(<slot />)で、名前がないものはデフォルトスロットと呼ばれます。

デフォルトスロットと名前付きスロットを一緒に使用したい

先ほどは名前付きSlotのみを使用しましたが、デフォルトスロットと名前付きスロットを一緒に使うことも可能です。
先ほどのカードの上部に下記のようにデフォルトスロットを用意しました。

<template>
  <div class="text-3xl"><slot /></div>
  <div class="max-w-sm rounded overflow-hidden shadow-lg">
    <div class="px-6 py-4">
      <div class="font-bold text-xl mb-2"><slot name="title" /></div>
      <p class="text-gray-700 text-base">
        <slot name="content" />
      </p>
    </div>
  </div>
</template>

このデフォルトスロットに対して、コンテンツを渡す場合は下記のようにv-slot:defaultを使用します。

<template>
  <my-card class="m-5">
    <template v-slot:default>デフォルトスロット!!</template>
    <template v-slot:title>食べ物カード</template>
    <template v-slot:content>
        <ul>
          <li>ラーメン</li>
          <li>ハンバーガー</li>
          <li>ピザ</li>
        </ul>
    </template>
  </my-card>
</template>

そうすると、このようにデフォルトスロットと名前付きスロットの内容が一緒に表示されることが確認できました。
Vue.jsでデフォルトスロットと名前付きスロットを一緒に使う

コメント

タイトルとURLをコピーしました