Vue3 教學 – 指令(Directives)
主題:v-slot(具名插槽)
簡介
在 Vue3 中,**插槽(slot)**是父子元件溝通的利器,讓子元件可以保留結構卻交由父元件自行填充內容。隨著應用規模的擴大,單純的預設插槽往往不足以表達多樣的 UI 結構,這時 具名插槽(named slot) 就顯得格外重要。
v-slot 指令是 Vue3 推出的統一語法,用來定義與取用具名插槽,取代了 Vue2 時期的 slot、slot-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>
說明:子元件把
items以 prop 的方式傳給插槽,父元件在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> 中撰寫較長內容,簡寫僅限單行或簡短片段 |
最佳實踐
- 保持插槽名稱語意化:如
header、footer、item,讓閱讀者一眼就能了解用途。 - 預設內容:在子元件
<slot>中提供合理的預設內容,減少父元件必填的情況。 - 使用解構賦值:取得作用域資料時,
v-slot="{ items }"比v-slot="slotProps"再手動slotProps.items更直觀。 - 避免深層嵌套:如果插槽層級超過 2 層,考慮改用 provide/inject 或 全域狀態管理(Pinia)傳遞資料。
- 文件化插槽 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 變數 |
這些情境中,具名插槽 能讓元件保持 封裝性 同時提供 彈性,減少大量的 props 與 emit,提升開發速度與程式碼可讀性。
總結
v-slot是 Vue3 中統一且功能強大的插槽指令,支援 具名插槽、作用域插槽 以及 動態插槽名稱。- 使用 具名插槽 可以在同一個子元件內提供多個可客製化的區塊,讓 UI 更具彈性。
- 透過 作用域插槽,子元件可以把資料直接傳給父元件,減少不必要的全域狀態或多層
props。 - 實作時要注意插槽名稱的唯一性、提供合理的預設內容,以及避免過度嵌套。
- 在表單、資料表格、Dialog、Dashboard 等常見 UI 元件中,
v-slot能顯著提升可重用性與維護性。
掌握 v-slot 後,你將能更自如地設計 Vue3 元件庫,寫出 乾淨、可讀、可擴充的程式碼。祝你在 Vue3 的開發旅程中玩得開心、寫得順手!