Vue.js: スロット
最終更新:2026-10-20
スロットは、Vueのコンテンツ配信APIの一部です。親コンポーネントは、子コンポーネントの「スロット」に任意のコンテンツを挿入できます。スロットにより、コンポーネントは「テンプレート」と同じくらい柔軟になります。つまり、親コンポーネントが表示内容を制御し、子コンポーネントが表示位置を制御する仕組みです。
Vue 3 では、デフォルトスロット、名前付きスロット、スコープ付きスロットの 3 種類のスロットが用意されています。このレッスンでは、これら 3 種類のスロットの使い方とベストプラクティスを習得できるよう解説します。
1. 学習内容
- デフォルトスロット
<slot />の基本的な使い方 <slot name="header" />およびv-slot:headerという名称のスロットの略称- スコープスロット:子コンポーネントは、親コンポーネントのスロットにデータを渡します
<template v-slot>構文 (Vue 2.6+)- 動的なスロット名と略語の構文
- デフォルトのスロットコンテンツ(フォールバックコンテンツ)
$slots/useSlotsフック
2. カードコンポーネントの内容をカスタマイズできないという課題
(1) 課題:カードスタイルが5種類あり、5つのコンポーネントを作成する必要がある
アリスのEコマース管理担当者は、5種類のカードを必要としていました:
VUE
<!-- ❌ The "Broken" Version:5 component -->
<!-- ProductCard.vue -->
<div class="card">{{ product.name }}</div>
<!-- UserCard.vue -->
<div class="card">{{ user.name }}</div>
<!-- OrderCard.vue -->
<div class="card">Order #{{ order.id }}</div>
<!-- 5 component,90% The code is the same,The only difference is the content -->
プロダクトマネージャーのチャーリー:
「アリス、来週はあと5種類のカードが必要なんだ。これ以上新しいコンポーネントを追加し続けるわけにはいかない。どんなコンテンツでも対応できる、柔軟性のあるカードが必要なんだ。」
(2) Vueのスロットを用いた解決策:1つのBaseCardで、コンテンツは親コンポーネントから提供される
1 BaseCard.vue → 無限の種類のカード。新しいカードを追加するたびに、コンテンツを記述するだけで済み、コンポーネントを変更する必要はありません。
(3) 収益
スロットを使用した後は:
- コンポーネント数:5 → 1(-80%)
- 新しいカードタイプ:テンプレートコード 1 行
- メンテナンスコスト:1つのスタイルを変更するだけで、5枚以上のカードが同時に更新される
- 再利用性:BaseCardは、カードに関連するあらゆる場面で利用できます
3. デフォルトのスロット
(1) 基本的な使い方
レンダリング結果:
HTML
<div class="card">
<h3>Hello</h3>
<p>This is the card content</p>
</div>
(2) デフォルトのコンテンツ(フォールバック)
VUE
<!-- The parent component does not pass content -->
<BaseCard />
<!-- Rendering:<p>No content provided</p> -->
<!-- Passing Content from the Parent Component -->
<BaseCard>
<p>Custom content</p>
</BaseCard>
<!-- Rendering:<p>Custom content</p>(Override Default)-->
(3) 5つの主なユースケース
| シナリオ | 用途 |
|---|---|
| カードの内容 | <slot /> |
| ボタンのテキスト | <slot /> |
| リスト項目 | <slot :item="item" /> |
| フォームフィールド | <slot /> |
| モーダルボックスの本文 | <slot /> |
▶ サンプル:BaseCard.vue(デフォルト+名前付きスロット)
▶ 試してみよう出力:
TEXT
親が渡したコンテンツがそのまま card-body に挿入される
何も渡さない場合は No content が表示される
header と footer スロットを渡したときだけ card-header と card-footer が描画される
4. 名前付きスロット
(1) 名前付きスロットの定義
(2) 親コンポーネントの使用(3つの構文)
(3) 動的なスロット名
(4) スロットの有無を確認する
▶ サンプル:BaseCardの5つの活用方法
▶ 試してみよう出力:
TEXT
1 → 本文のみ(header と footer なし)
2 → ヘッダー Product Name + 本文
3 → ヘッダー・本文・View Details ボタンの 3 点セット
4 → v-slot の完全構文でも同じ、5 → actions スロットに Edit と Delete
5. スコープスロット
(1) 要点:子コンポーネントは、親コンポーネントのスロットにデータを渡す
レンダリング結果:
HTML
<ul>
<li><p>1. Alice (alice@example.com)</p></li>
<li><p>2. Bob (bob@example.com)</p></li>
<li><p>3. Charlie (charlie@example.com)</p></li>
</ul>
(2) 5つのスコープスロットの配置パターン
| パターン | 親コンポーネントの実装 | 目的 |
|---|---|---|
| すべてのプロップを受け入れる | <template #default="slotProps"> |
オブジェクト全体を受け入れる |
| 脱構築 | <template #default="{ user }"> |
必要なフィールドのみを使用する |
| 名前の変更 | <template #default="{ user: u }"> |
変数の競合を避ける |
| デフォルト値 | <template #default="{ user = defaultUser }"> |
デコンストラクション時にデフォルト値を指定する |
| テンプレートなし | {{ slotProps.user.name }} |
シンプルなシナリオ(デフォルトのスロット) |
(3) 完全な例:カスタマイズ可能なリスト
6. $slots と useSlots
(1) $slots スロットへのアクセス
(2) useSlots(Composition API)
VUE
<script setup>
import { useSlots } from 'vue'
const slots = useSlots()
// Check if the slot exists
if (slots.header) {
console.log('Header slot exists')
}
// Dynamic Rendering
if (slots.default) {
console.log('Default slot exists')
}
</script>
(3) $slots 対 useSlots
| 寸法 | $slots |
useSlots() |
|---|---|---|
| APIの種類 | テンプレート内で直接使用 | JS(Composition API)で使用 |
| 戻る | スロットオブジェクト | スロットオブジェクト(refs形式) |
| シーン | テンプレート内 | <script setup> 内 |
▶ サンプル:$slots の主な5つの用途
▶ 試してみよう出力:
TEXT
header スロットがあれば Has header を表示
渡されたスロット名が列挙され、それぞれ slot :name で描画される
default が無い場合は Default fallback が表示される
7. 総合演習:スコープスロットのデータテーブル
▶ サンプル:スコープスロットの完全な例
▶ 試してみよう出力:
TEXT
status 列は #cell-status により badge 付きの span に置き換わる
actions 列は Edit ボタンだけが描画される
スロットを渡していない列は row[col.key] の生の値がそのまま出る
(1) 📊 . よくある5つの間違いのクイックリファレンス
| エラー | 症状 | 解決策 |
|---|---|---|
| スロット名のスペルミス | コンテンツが表示されない | <slot name="..."> と #name が一致しているか確認してください |
| 親コンポーネント内に複数のルート要素が存在します | 警告 | 親コンポーネントが <template #name> で囲まれています |
| v-slot(旧構文) | Vue 2 対応 | 代わりに v-slot:header または #header を使用してください |
| デフォルトのスロットが渡されていない | 親コンポーネントが表示されていない | 親コンポーネント内の <Component /> の内容を確認してください |
| スコープスロットはデストラクチャリングされない | テンプレートは冗長である | { user, index } によるデストラクチャリング |
(2) 📊 . 5 性能比較
| 構文 | コンパイル | パフォーマンス | 推奨事項 |
|---|---|---|---|
<slot /> |
静的 | ⭐⭐⭐⭐⭐ | デフォルト |
<slot name="x" /> |
静的 | ⭐⭐⭐⭐⭐ | 命名済み |
<slot :x="x" /> |
静的 | ⭐⭐⭐⭐ | スコープ |
v-if="$slots.x" |
状態 | ⭐⭐⭐⭐ | 検出済み |
useSlots() |
実行時間 | ⭐⭐⭐ | JSで使用 |
❓ よくある質問
Q スロットとプロップの違いは何ですか?
A プロップはデータ(任意の JavaScript 値)を渡すのに対し、スロットはコンテンツ(HTML やコンポーネントの断片)を渡します。プロップは設定オプションに適しており、スロットはコンテンツのカスタマイズに適しています。
Q スコープスロットはどのような場合に使用すべきですか?
A 親コンポーネントが、子コンポーネントからのデータに基づいてレンダリングをカスタマイズする必要がある場合です。例えば、テーブルの列のレンダリング、リスト項目のレンダリング、またはカードコンテンツのレイアウトなどです。
Q
v-slot と # の省略形にはどのような違いがありますか?A これらは完全に同等です。
#header is a shorthand for v-slot:header。省略形の使用をお勧めします(より簡潔だからです)。Q デフォルトのスロットに名前を付けることはできますか?
A デフォルトのスロットに名前を付ける必要はありません。デフォルトは
<slot /> です。どうしても名前を付けなければならない場合は、<slot name="default" /> を使用してください。親コンポーネントでは <template #default> を使用するか、単にコンテンツを直接渡すことができます。Q スロットをプロップとして渡すことはできますか?
A スロットを直接プロップとして渡すことはできません。ただし、VNode やレンダリング関数をプロップとして渡すことは可能です(上級者向け)。ほとんどの場合は、単に
<slot> を使用してください。Q 親コンポーネント内で子コンポーネントのスロットを検出するにはどうすればよいですか?
A
$slots(テンプレート)または useSlots()(JS)を使用します。子コンポーネントは <slot> を使用してスロットを公開し、親コンポーネントは <template #name> を使用してそのスロットに値を設定します。Q 名前付きスロットにデフォルトの内容を設定できますか?
A はい。
<slot name="header"><h3>Default Header</h3></slot>。これは、親コンポーネントがヘッダーを渡さない場合に表示されます。📖 まとめ
- スロットは Vue のコンテンツ配置 API です。親コンポーネントがコンテンツを制御し、子コンポーネントが配置を制御します。
- 3種類のスロット:デフォルトスロット(
<slot />)、名前付きスロット(name="x")、スコープ付きスロット(:x="x") - 親コンポーネントは、
<template #name>またはその略称である#nameを使用して、名前付きスロットを渡します。 - スコープスロットを使用すると、子コンポーネントから親コンポーネントへデータを渡すことができます(スロットプロパティ)。
<slot>親コンポーネントからコンテンツが渡されない場合に表示されるデフォルトのコンテンツ(フォールバック)$slots/useSlots()スロットが存在するか確認する- スロットとプロップの扱い:プロップは設定を渡し、スロットはコンテンツを渡す
📝 練習問題
-
基礎問題(難易度:⭐)
簡単なButtonコンポーネントを実装します:
- BaseButton.vue:デフォルトのスロット + 3つのバリエーション(primary/success/danger)
- props:
variant(文字列) - 親コンポーネント:3つのバリエーションとカスタムスロットコンテンツをテスト
-
上級問題(難易度:⭐⭐)
BaseLayout コンポーネントを実装します:
- 子コンポーネント:3つの名前付きスロット(ヘッダー/デフォルト/フッター)
- 親コンポーネント:ダッシュボードページを実装するには、BaseLayout を使用してください
$slotsを使用して、スロットが存在するかどうかを確認してください- デフォルトのコンテンツ(フォールバック)を追加する
-
チャレンジ問題(難易度:⭐⭐⭐)
完全なDataTableコンポーネントシステムを実装する:
- DataTable.vue:列とデータを受け取り、テーブルを表示する
- スコープスロット:
#cell-{key}親コンポーネントがセルをカスタマイズできるようにします - デフォルトのスロット:親コンポーネントが値を指定していない場合に、元の値を表示します
- 親コンポーネントの例:ユーザーテーブルと注文テーブル――2つの異なる列のレンダリング方法
- パフォーマンス:v-memoは行をキャッシュします(データに変更がない場合は再描画をスキップします)
- TypeScriptの厳格な型付け