Skip to content

Vue 具名作用域插槽 (Named Scoped Slots) 詳解:打造頂級靈活的元件接口

Vue 3 具名作用域插槽:#name 與解構語法

在 Vue 的插槽系統中,具名插槽 解決了 在哪裡渲染 的問題,而 作用域插槽 解決了 用什麼資料渲染 的問題。當我們將兩者結合,就進化成了 具名作用域插槽,它允許您在一個元件中開放多個特定區域,並分別將內部的私有資料傳遞出來給父元件自定義。

核心觀念:雙劍合璧

具名作用域插槽的核心在於:子元件在不同的 <slot> 中綁定不同的屬性(Attributes),父元件則透過 v-slot:插槽名="資料物件" 的語法來接收。

  • 具名 (Named):使用 name="xxx" 標識位置。
  • 作用域 (Scoped):使用 :data="value" 進行屬性綁定。
  • 父元件接收:使用 #name="{ data }" (解構寫法) 進行操作。

實作步驟

  1. 子元件定義:在特定的 <slot> 標籤上同時設定 name 屬性與資料綁定。
  2. 父元件呼叫:使用 <template> 標籤並指定插槽名稱。
  3. 接收參數:在 <template> 標籤的 v-slot 指令中定義參數名稱(或進行解構)。
  4. 定義 UI:利用接收到的資料決定該區塊的 HTML 結構。

實作範例:複雜資料清單

假設我們要開發一個 ProductList.vue 元件,它負責顯示產品列表,並開放產品名稱與操作按鈕兩個區塊給父元件自定義,且兩個區塊都需要存取目前的產品資料。

子元件:ProductList.vue

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

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

vue
<!-- 子元件綁的是 :item -->
<slot name="title" :item="item"></slot>
vue
<!-- ❌ 拿不到值,子元件沒有綁 product -->
<template #title="{ product }">{{ product.name }}</template>

<!-- ✅ 想換名字用重新命名的寫法 -->
<template #title="{ item: product }">{{ product.name }}</template>

不確定子元件傳出哪些欄位時,最快的方法是先不解構,直接把整包印出來看:

vue
<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> 上的資料,所以列表不會因為沒填插槽而空白。

一個元件開放太多具名作用域插槽會有什麼問題?

父元件的模板會迅速膨脹,也很難一眼看出每個插槽拿到什麼資料。超過五個插槽時,通常代表這個元件承擔了太多責任,應該考慮拆成更小的元件。

延伸閱讀