本文 AI 產出,尚未審核
Vue3 元件基礎:Slot 插槽(default slot、named slot)
簡介
在 Vue3 中,Slot 是組件溝通的關鍵機制之一。它允許父層在使用子元件時,將任意的 HTML 或其他子元件「投射」進子元件的特定位置。藉由 Slot,我們可以把元件寫得更具彈性、可重用度更高,同時也能保持乾淨的結構,避免硬編碼大量樣板程式碼。
本篇文章將從 預設插槽(default slot)、具名插槽(named slot) 兩大主題切入,說明概念、展示實作範例,並提供常見陷阱與最佳實踐,幫助你在實務專案中快速上手與應用。
核心概念
1. 預設插槽(default slot)
預設插槽是最簡單的插槽形式,子元件只需要在模板中放置 <slot></slot>,父層則直接在子元件標籤內寫入要投射的內容。若父層未提供內容,預設插槽會顯示 fallback(備援)內容。
範例 1:最簡單的預設插槽
<!-- Card.vue -->
<template>
<div class="card">
<header class="card-header">
<slot name="header">預設標題</slot>
</header>
<section class="card-body">
<slot>這裡是預設內容</slot> <!-- default slot -->
</section>
</div>
</template>
<!-- 使用 Card.vue -->
<Card>
<!-- 直接放入預設 slot 的內容 -->
<p>這段文字會被投射到 <slot> 的位置。</p>
</Card>
解說:
<slot>標籤會被<p>內容取代,若<Card>沒有子元素,則會顯示 「這裡是預設內容」。
範例 2:帶 fallback 的預設插槽
<!-- Alert.vue -->
<template>
<div class="alert">
<slot>
<strong>注意!</strong> 這是預設訊息。
</slot>
</div>
</template>
<!-- 不提供任何子內容 -->
<Alert></Alert> <!-- 會顯示 fallback 文字 -->
<!-- 提供自訂內容 -->
<Alert>
<em>系統已完成更新。</em>
</Alert>
2. 具名插槽(named slot)
具名插槽允許在同一個元件內同時放置多個插槽,每個插槽都有自己的 name,父層必須使用 v-slot(或縮寫 #)指定要投射到哪個具名插槽。這讓子元件能夠在不同區塊接受不同的內容。
範例 3:兩個具名插槽(header & footer)
<!-- Modal.vue -->
<template>
<div class="modal">
<header class="modal-header">
<slot name="header">預設標題</slot>
</header>
<section class="modal-body">
<slot>預設內容</slot>
</section>
<footer class="modal-footer">
<slot name="footer">
<button @click="$emit('close')">關閉</button>
</slot>
</footer>
</div>
</template>
<!-- 使用 Modal.vue -->
<Modal>
<template #header>
<h2>自訂標題</h2>
</template>
<p>這裡是主要內容。</p>
<template #footer>
<button @click="save">儲存</button>
<button @click="$emit('close')">取消</button>
</template>
</Modal>
解說:
#header、#footer為具名插槽的縮寫寫法。- 若父層未提供
header或footer,則會使用<slot>內的 fallback。
範例 4:動態具名插槽(使用 v-slot 變數)
<!-- List.vue -->
<template>
<ul>
<li v-for="item in items" :key="item.id">
<slot name="item" :item="item">
{{ item.text }} <!-- fallback -->
</slot>
</li>
</ul>
</template>
<script setup>
defineProps({ items: Array })
</script>
<!-- 呼叫 List.vue,取得每筆資料的自訂呈現 -->
<List :items="todoList">
<template #item="{ item }">
<input type="checkbox" v-model="item.done">
<span :class="{ done: item.done }">{{ item.title }}</span>
</template>
</List>
重點:具名插槽支援 作用域插槽(scoped slot),父層可以透過解構取得子元件傳遞的屬性(此例為 item)。
範例 5:混合預設與具名插槽
<!-- Panel.vue -->
<template>
<section class="panel">
<header class="panel-header">
<slot name="title">預設標題</slot>
</header>
<div class="panel-content">
<slot>預設內容</slot>
</div>
</section>
</template>
<!-- 只想改寫 title,內容保留預設 -->
<Panel>
<template #title>自訂標題</template>
</Panel>
常見陷阱與最佳實踐
| 陷阱 | 說明 | 解決方案 |
|---|---|---|
| 忘記提供 fallback | 當父層未投射內容時,插槽會變成空白,可能破壞排版。 | 為每個 <slot> 加上合理的 fallback(文字、圖示或結構)。 |
| 具名插槽名稱拼寫錯誤 | name="header" 與 #header 必須完全一致,大小寫敏感。 |
使用常量或 TypeScript enum 來管理插槽名稱,降低錯字機率。 |
| 作用域插槽未解構 | 直接使用 slot 內容會拿不到子元件傳遞的屬性。 |
以 <template #slotName="{ prop }"> 方式解構,或使用 v-slot 的完整語法。 |
| 過度使用多層插槽 | 多層嵌套的插槽會讓模板難以閱讀與除錯。 | 儘量將複雜 UI 拆成小元件,僅在必要時使用 具名 + 預設混合。 |
| 插槽內容無法響應式 | 若在插槽內直接寫死文字,無法隨父層狀態變化。 | 讓插槽內容本身也是 Vue 元件或使用 v-bind 傳遞響應式資料。 |
最佳實踐
- 明確命名:使用語意化的插槽名稱(如
header、footer、item),讓使用者一眼就能看出目的。 - 提供 fallback:即使是最簡單的 UI,也應該給出預設文字或結構,避免空白。
- 限制插槽數量:一個元件建議不超過 3~4 個插槽,超過後考慮拆分元件。
- 使用
v-slot簡寫:在 Vue3 中,#前綴是最直觀的寫法,減少樣板代碼。 - 文件化:在元件的 JSDoc 或 README 中註明每個插槽的功能、預期傳入的 props,提升可維護性。
實際應用場景
- 表單欄位的自訂顯示
- 透過具名插槽
label、error,讓表單元件(如<InputField>)在不同頁面可自行決定標籤與錯誤訊息的樣式。
- 透過具名插槽
- 彈窗與對話框
- 使用
header、default、footer三個插槽,可快速組合出「確認」或「資訊」類型的對話框,而不需要重寫整個結構。
- 使用
- 列表與資料表
- 透過作用域插槽把每筆資料的呈現交給父層決定,支援多樣化的 UI(卡片、表格、網格)。
- 版面布局(Layout)
- 主框架提供
sidebar、main、footer插槽,子頁面只需要投射自己的內容即可,保持全局樣式一致。
- 主框架提供
總結
Slot 是 Vue3 中 組件化 的核心工具之一,透過 default slot、named slot 以及 作用域插槽,我們可以把 UI 抽離成高度可重用、彈性十足的元件。本文從概念說明、實作範例、常見陷阱與最佳實踐,最後列出日常開發中常見的應用場景,提供了完整的學習藍圖。
掌握好 Slot 後,你的 Vue 專案將能更快地 分離關注點、提升維護性,也更容易在團隊中 共享 UI 樣式。快把這些技巧帶回你的專案裡,立即感受開發效率的提升吧!