本文 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 為具名插槽的縮寫寫法。
  • 若父層未提供 headerfooter,則會使用 <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 傳遞響應式資料。

最佳實踐

  1. 明確命名:使用語意化的插槽名稱(如 headerfooteritem),讓使用者一眼就能看出目的。
  2. 提供 fallback:即使是最簡單的 UI,也應該給出預設文字或結構,避免空白。
  3. 限制插槽數量:一個元件建議不超過 3~4 個插槽,超過後考慮拆分元件。
  4. 使用 v-slot 簡寫:在 Vue3 中,# 前綴是最直觀的寫法,減少樣板代碼。
  5. 文件化:在元件的 JSDoc 或 README 中註明每個插槽的功能、預期傳入的 props,提升可維護性。

實際應用場景

  1. 表單欄位的自訂顯示
    • 透過具名插槽 labelerror,讓表單元件(如 <InputField>)在不同頁面可自行決定標籤與錯誤訊息的樣式。
  2. 彈窗與對話框
    • 使用 headerdefaultfooter 三個插槽,可快速組合出「確認」或「資訊」類型的對話框,而不需要重寫整個結構。
  3. 列表與資料表
    • 透過作用域插槽把每筆資料的呈現交給父層決定,支援多樣化的 UI(卡片、表格、網格)。
  4. 版面布局(Layout)
    • 主框架提供 sidebarmainfooter 插槽,子頁面只需要投射自己的內容即可,保持全局樣式一致。

總結

Slot 是 Vue3 中 組件化 的核心工具之一,透過 default slotnamed slot 以及 作用域插槽,我們可以把 UI 抽離成高度可重用、彈性十足的元件。本文從概念說明、實作範例、常見陷阱與最佳實踐,最後列出日常開發中常見的應用場景,提供了完整的學習藍圖。
掌握好 Slot 後,你的 Vue 專案將能更快地 分離關注點提升維護性,也更容易在團隊中 共享 UI 樣式。快把這些技巧帶回你的專案裡,立即感受開發效率的提升吧!