Vue 具名作用域插槽 (Named Scoped Slots) 詳解:打造頂級靈活的元件接口
在 Vue 的插槽系統中,具名插槽 解決了 在哪裡渲染 的問題,而 作用域插槽 解決了 用什麼資料渲染 的問題。當我們將兩者結合,就進化成了 具名作用域插槽,它允許您在一個元件中開放多個特定區域,並分別將內部的私有資料傳遞出來給父元件自定義。
核心觀念:雙劍合璧
具名作用域插槽的核心在於:子元件在不同的 <slot> 中綁定不同的屬性(Attributes),父元件則透過 v-slot:插槽名="資料物件" 的語法來接收。
- 具名 (Named):使用
name="xxx"標識位置。 - 作用域 (Scoped):使用
:data="value"進行屬性綁定。 - 父元件接收:使用
#name="{ data }"(解構寫法) 進行操作。
實作步驟
- 子元件定義:在特定的
<slot>標籤上同時設定 name 屬性與資料綁定。 - 父元件呼叫:使用
<template>標籤並指定插槽名稱。 - 接收參數:在
<template>標籤的v-slot指令中定義參數名稱(或進行解構)。 - 定義 UI:利用接收到的資料決定該區塊的 HTML 結構。
實作範例:複雜資料清單
假設我們要開發一個 ProductList.vue 元件,它負責顯示產品列表,並開放產品名稱與操作按鈕兩個區塊給父元件自定義,且兩個區塊都需要存取目前的產品資料。
子元件:ProductList.vue
<script setup>
const products = [
{ id: 1, name: "咖啡機", price: 5000, stock: 10 },
{ id: 2, name: "磨豆機", price: 2500, stock: 0 },
];
</script>
<template>
<div class="list-container">
<div v-for="item in products" :key="item.id" class="item-row">
<slot name="title" :item="item"> {{ item.name }} (預設) </slot>
<slot name="action" :item="item" :isOut="item.stock === 0">
<button>查看詳情</button>
</slot>
</div>
</div>
</template>父元件:App.vue
<template>
<ProductList>
<template #title="{ item }">
<h3 style="color: blue;">{{ item.name }} - NT${{ item.price }}</h3>
</template>
<template #action="{ item, isOut }">
<button :disabled="isOut">
{{ isOut ? "補貨中" : `購買 (${item.stock})` }}
</button>
</template>
</ProductList>
</template>值得注意的是,兩個插槽 傳出的資料並不相同:title 只需要項目本身,action 則額外拿到了 isOut 這個 已經判斷好的結果。把判斷留在子元件、把呈現交給父元件,是這個模式最舒服的分工。
命名必須對得上
解構時的 key 就是子元件綁定的屬性名稱,寫錯就會拿到 undefined:
<!-- 子元件綁的是 :item -->
<slot name="title" :item="item"></slot><!-- ❌ 拿不到值,子元件沒有綁 product -->
<template #title="{ product }">{{ product.name }}</template>
<!-- ✅ 想換名字用重新命名的寫法 -->
<template #title="{ item: product }">{{ product.name }}</template>不確定子元件傳出哪些欄位時,最快的方法是先不解構,直接把整包印出來看:
<template #title="slotProps">
<pre>{{ slotProps }}</pre>
</template>為什麼要使用具名作用域插槽?
極致的 UI 控制權
子元件負責繁瑣的邏輯,例如:v-for、API 請求、狀態判斷,父元件則能像拼圖一樣決定每個關鍵位置的長相。
避免元件臃腫
不需要透過無數的 props,例如:titleColor, showBuyButton 來控制 UI,直接開放插槽讓父元件處理最乾淨。
強大的複用性
同一個列表元件,在 A 頁面可以顯示為圖卡,在 B 頁面可以顯示為簡易表格,只需替換插槽內容。
注意事項
- 避免命名衝突:確保子元件中 slot 綁定的屬性名稱與父元件解構的變數名稱一致。
- 預設值的設置:建議在子元件的 slot 內部撰寫預設內容,以防父元件未傳入內容時頁面空無一物。
- 適度使用:雖然靈活,但若一個元件有超過 5 個具名作用域插槽,可能會讓父元件的模板變得過於複雜,此時應考慮是否需拆分元件。
若插槽的名稱還需要由資料決定,例如:欄位由設定檔產生,可以再往前一步搭配 動態插槽。
常見問題
具名作用域插槽的語法怎麼寫?
子元件在 <slot> 上同時寫 name 與資料綁定,例如:<slot name="title" :item="item">。父元件則用 <template #title="{ item }"> 接收,等同於 v-slot:title。
每個插槽可以傳出不同的資料嗎?
可以,每個 <slot> 的綁定都是獨立的。例如:標題插槽只傳出項目本身,操作按鈕插槽可以額外傳出是否缺貨這類判斷結果。
解構的變數名稱可以自己取嗎?
解構時的 key 必須與子元件綁定的屬性名稱一致,否則會拿到 undefined。若想換名字,可以用 { item: product } 這種重新命名的寫法。
父元件沒有填某個具名作用域插槽會怎樣?
會顯示子元件寫在 <slot> 之間的預設內容,而預設內容同樣能使用綁在該 <slot> 上的資料,所以列表不會因為沒填插槽而空白。
一個元件開放太多具名作用域插槽會有什麼問題?
父元件的模板會迅速膨脹,也很難一眼看出每個插槽拿到什麼資料。超過五個插槽時,通常代表這個元件承擔了太多責任,應該考慮拆成更小的元件。