Skip to content

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

在 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>

為什麼要使用具名作用域插槽?

極致的 UI 控制權

子元件負責繁瑣的邏輯,例如 v-for、API 請求、狀態判斷,父元件則能像「拼圖」一樣決定每個關鍵位置的長相。

避免元件臃腫

不需要透過無數的 props,例如 titleColor, showBuyButton 來控制 UI,直接開放插槽讓父元件處理最乾淨。

強大的複用性

同一個列表元件,在 A 頁面可以顯示為圖卡,在 B 頁面可以顯示為簡易表格,只需替換插槽內容。

注意事項

  • 避免命名衝突:確保子元件中 slot 綁定的屬性名稱與父元件解構的變數名稱一致。
  • 預設值的設置:建議在子元件的 slot 內部撰寫預設內容,以防父元件未傳入內容時頁面空無一物。
  • 適度使用: 雖然靈活,但若一個元件有超過 5 個具名作用域插槽,可能會讓父元件的樣板變得過於複雜,此時應考慮是否需拆分元件。