本文 AI 產出,尚未審核

Vue3 教學 – 指令(Directives)

主題:v-slot(具名插槽)


簡介

在 Vue3 中,**插槽(slot)**是父子元件溝通的利器,讓子元件可以保留結構卻交由父元件自行填充內容。隨著應用規模的擴大,單純的預設插槽往往不足以表達多樣的 UI 結構,這時 具名插槽(named slot) 就顯得格外重要。

v-slot 指令是 Vue3 推出的統一語法,用來定義與取用具名插槽,取代了 Vue2 時期的 slotslot-scope 等寫法。掌握 v-slot 後,你可以在同一個子元件中同時提供多個可自行客製化的區塊,讓 UI 的可重用性與可維護性大幅提升。

本篇文章將從概念說明、實作範例、常見陷阱與最佳實踐,最後帶你了解在真實專案中如何運用 v-slot,幫助你從 初學者 成長為 中級開發者


核心概念

1. 基本語法

在 Vue3,v-slot 只是一個 指令,可以寫在子元件的 <template> 標籤或是直接寫在子元件的開頭標籤上。最簡單的具名插槽語法如下:

<!-- 子元件 MyCard.vue -->
<template>
  <div class="card">
    <header class="card-header">
      <!-- 具名插槽 header -->
      <slot name="header">預設標題</slot>
    </header>
    <section class="card-body">
      <!-- 預設插槽 -->
      <slot>預設內容</slot>
    </section>
    <footer class="card-footer">
      <!-- 具名插槽 footer -->
      <slot name="footer">預設腳註</slot>
    </footer>
  </div>
</template>

父元件使用時:

<MyCard>
  <template v-slot:header>
    <h2>自訂標題</h2>
  </template>

  <p>這裡是卡片的主要內容。</p>

  <template v-slot:footer>
    <small>© 2025 My Company</small>
  </template>
</MyCard>

重點v-slot:header 中的 header 必須與子元件 <slot name="header">name 完全對應。

2. 簡寫語法

如果插槽只需要 單行內容,可以直接在子元件標籤上使用簡寫:

<MyCard v-slot:header="<h2>簡寫標題</h2>">
  <p>內容…</p>
</MyCard>

這種寫法在簡單場景下能減少 <template> 包裝,提升可讀性。

3. 具名插槽搭配作用域(Scoped Slots)

v-slot 也支援 作用域插槽,即子元件可以把資料「傳」給父元件,父元件在插槽內取得並使用。語法如下:

<!-- 子元件 DataProvider.vue -->
<template>
  <slot :items="items"></slot>
</template>

<script setup>
import { ref } from 'vue'
const items = ref(['Apple', 'Banana', 'Cherry'])
</script>

父元件:

<DataProvider v-slot="{ items }">
  <ul>
    <li v-for="(it, i) in items" :key="i">{{ it }}</li>
  </ul>
</DataProvider>

此範例展示 解構 v-slot 取得的 items,並在父元件中渲染清單。

4. 多個具名插槽的組合

實務上,常會同時使用多個具名插槽與預設插槽,組合成彈性版型:

<!-- 子元件 Layout.vue -->
<template>
  <div class="layout">
    <aside class="sidebar"><slot name="sidebar"></slot></aside>
    <main class="main"><slot></slot></main>
    <nav class="nav"><slot name="nav"></slot></nav>
  </div>
</template>

父元件使用:

<Layout>
  <template v-slot:sidebar>
    <ul><li>選單 1</li><li>選單 2</li></ul>
  </template>

  <h1>主內容標題</h1>
  <p>這裡是主要內容區塊。</p>

  <template v-slot:nav>
    <button>上一頁</button>
    <button>下一頁</button>
  </template>
</Layout>

5. 動態插槽名稱

有時插槽名稱需要根據變數決定,Vue3 允許 動態插槽名稱(dynamic slot name):

<!-- 子元件 DynamicTabs.vue -->
<template>
  <div class="tabs">
    <slot :name="activeTab"></slot>
  </div>
</template>

<script setup>
import { ref } from 'vue'
const activeTab = ref('tabA')
</script>

父元件:

<DynamicTabs>
  <template v-slot:tabA>內容 A</template>
  <template v-slot:tabB>內容 B</template>
</DynamicTabs>

切換 activeTab 時,顯示的插槽內容會自動切換。


程式碼範例

以下提供 5 個實用範例,從簡單到進階,協助你快速上手 v-slot

範例 1:基本具名插槽

// Card.vue
<template>
  <div class="card">
    <slot name="title"><h3>預設標題</h3></slot>
    <slot>預設內容</slot>
  </div>
</template>
<!-- 使用 Card.vue -->
<Card>
  <template v-slot:title>
    <h3>自訂標題</h3>
  </template>
  <p>這裡是自訂內容。</p>
</Card>

說明v-slot:title 替代了 <slot name="title">,父元件自行決定標題顯示。

範例 2:具名插槽+作用域資料

// ListProvider.vue
<template>
  <slot :items="items"></slot>
</template>

<script setup>
import { ref } from 'vue'
const items = ref(['Vue', 'React', 'Angular'])
</script>
<!-- 父元件 -->
<ListProvider v-slot="{ items }">
  <ol>
    <li v-for="(item, i) in items" :key="i">{{ i + 1 }}. {{ item }}</li>
  </ol>
</ListProvider>

說明:子元件把 itemsprop 的方式傳給插槽,父元件在 v-slot 中解構取得。

範例 3:多具名插槽的完整布局

// Dashboard.vue
<template>
  <div class="dashboard">
    <header class="header"><slot name="header"></slot></header>
    <aside class="sidebar"><slot name="sidebar"></slot></aside>
    <main class="content"><slot></slot></main>
    <footer class="footer"><slot name="footer"></slot></footer>
  </div>
</template>
<Dashboard>
  <template v-slot:header><h1>儀表板</h1></template>
  <template v-slot:sidebar>
    <ul><li>概覽</li><li>報表</li></ul>
  </template>

  <p>這裡是主要內容區塊。</p>

  <template v-slot:footer><small>© 2025</small></template>
</Dashboard>

說明:一次使用四個具名插槽與預設插槽,呈現完整的頁面骨架。

範例 4:簡寫語法 + 單行內容

<!-- 在父元件中 -->
<MyCard v-slot:header="<h2>單行標題</h2>">
  <p>卡片內容。</p>
</MyCard>

說明:如果插槽內容很簡短,可直接寫在 v-slot 的屬性值中,免去 <template> 包裝。

範例 5:動態切換插槽

// TabSwitcher.vue
<template>
  <div>
    <button @click="active = 'first'">First</button>
    <button @click="active = 'second'">Second</button>

    <slot :name="active"></slot>
  </div>
</template>

<script setup>
import { ref } from 'vue'
const active = ref('first')
</script>
<TabSwitcher>
  <template v-slot:first>第一頁內容</template>
  <template v-slot:second>第二頁內容</template>
</TabSwitcher>

說明active 變數決定目前顯示哪個具名插槽,實現簡易的 Tab 切換功能。


常見陷阱與最佳實踐

陷阱 可能的結果 解決方案
忘記在子元件設定 name 父元件的 v-slot:xxx 不會渲染,出現預設內容或空白 確認 <slot name="xxx"> 已正確命名
同名插槽出現在多個子元件 會產生覆蓋或不預期的渲染順序 使用 唯一的插槽名稱,或在父層包裝不同子元件
作用域變數命名衝突 變數被覆寫,導致資料錯亂 使用 解構賦值別名v-slot="{ items: list }"
過度使用具名插槽 結構變得過於複雜,維護成本升高 僅在需要多區塊客製化時使用,否則使用預設插槽或組件 Prop
v-slot 中直接寫 HTML(簡寫) 大段 HTML 難以排版與除錯 盡量在 <template> 中撰寫較長內容,簡寫僅限單行或簡短片段

最佳實踐

  1. 保持插槽名稱語意化:如 headerfooteritem,讓閱讀者一眼就能了解用途。
  2. 預設內容:在子元件 <slot> 中提供合理的預設內容,減少父元件必填的情況。
  3. 使用解構賦值:取得作用域資料時,v-slot="{ items }"v-slot="slotProps" 再手動 slotProps.items 更直觀。
  4. 避免深層嵌套:如果插槽層級超過 2 層,考慮改用 provide/inject全域狀態管理(Pinia)傳遞資料。
  5. 文件化插槽 API:在元件說明文件中列出所有具名插槽與可傳遞的作用域屬性,提升團隊協作效率。

實際應用場景

場景 為何使用 v-slot 範例說明
表單元件套件(如 <FormItem> 讓使用者自行定義標籤、說明文字或錯誤訊息 <FormItem v-slot:label>姓名</FormItem>
資料表格(DataTable) 每一列的操作列(編輯、刪除)需要自訂 <DataTable v-slot:action="{ row }">…</DataTable>
彈出層/Dialog 標題、內容、按鈕皆可客製化 <Dialog v-slot:header>確認刪除</Dialog>
版面布局(Dashboard、Admin Panel) 讓不同頁面自行注入側邊欄、工具列等 <Layout v-slot:sidebar>…</Layout>
動態 Tab / Accordion 根據使用者操作切換顯示的插槽 TabSwitcher 範例中的 active 變數

這些情境中,具名插槽 能讓元件保持 封裝性 同時提供 彈性,減少大量的 propsemit,提升開發速度與程式碼可讀性。


總結

  • v-slot 是 Vue3 中統一且功能強大的插槽指令,支援 具名插槽作用域插槽 以及 動態插槽名稱
  • 使用 具名插槽 可以在同一個子元件內提供多個可客製化的區塊,讓 UI 更具彈性。
  • 透過 作用域插槽,子元件可以把資料直接傳給父元件,減少不必要的全域狀態或多層 props
  • 實作時要注意插槽名稱的唯一性、提供合理的預設內容,以及避免過度嵌套。
  • 在表單、資料表格、Dialog、Dashboard 等常見 UI 元件中,v-slot 能顯著提升可重用性維護性

掌握 v-slot 後,你將能更自如地設計 Vue3 元件庫,寫出 乾淨、可讀、可擴充的程式碼。祝你在 Vue3 的開發旅程中玩得開心、寫得順手!