Vue 中 slot 的使用方法

在 Vue 組件化開發中,slot(插槽)就像組件預留的 “靈活接口”,讓父組件能向子組件的指定位置插入自定義內容,既保留了子組件的結構複用,又賦予了內容定製的靈活性,避免了組件過於僵硬。無論是簡單的文本插入,還是複雜的組件嵌套,slot 都能輕鬆應對,是組件複用與定製的核心工具。

最基礎的是默認插槽,子組件中預留一個未命名的插槽,父組件在使用子組件時,直接在標籤內部寫入的內容,都會被渲染到這個插槽位置。比如一個通用的卡片組件,需要父組件自定義卡片內容:

<!-- 子組件 Card.vue -->
<template>
  <div class="card">
    <div class="card-header">
      <h3>通用卡片</h3>
    </div>
    <!-- 默認插槽:父組件內容會插入這裏 -->
    <slot></slot>
    <div class="card-footer">
      <button>操作按鈕</button>
    </div>
  </div>
</template>

<!-- 父組件 App.vue -->
<template>
  <div class="app">
    <h2>默認插槽示例</h2>
    <!-- 向默認插槽插入自定義內容 -->
    <Card>
      <p>這是父組件插入的卡片內容</p>
      <p>可以是文本、標籤或其他組件</p>
      <img src="https://picsum.photos/200/100" alt="示例圖片">
    </Card>
  </div>
</template>

<script setup>
import Card from './Card.vue'
</script>

這裏子組件的<slot>標籤就是默認插槽的入口,父組件在<Card>標籤內寫入的所有內容,都會替換<slot>的位置,既複用了卡片的頭部和底部結構,又實現了內容的自定義。

當子組件需要多個自定義區域時,就需要用到具名插槽。給每個插槽命名,父組件通過v-slot:插槽名(簡寫為#插槽名)指定內容插入的位置,精準控制不同區域的顯示:

<!-- 子組件 Layout.vue -->
<template>
  <div class="layout">
    <!-- 頭部插槽:命名為header -->
    <slot name="header"></slot>
    <!-- 主體插槽:命名為main -->
    <slot name="main"></slot>
    <!-- 底部插槽:命名為footer -->
    <slot name="footer"></slot>
  </div>
</template>

<!-- 父組件 App.vue -->
<template>
  <div class="app">
    <h2>具名插槽示例</h2>
    <Layout>
      <!-- 向header插槽插入內容 -->
      <template #header>
        <div class="layout-header">
          <h3>頁面標題</h3>
          <nav>導航菜單</nav>
        </div>
      </template>
      <!-- 向main插槽插入內容 -->
      <template #main>
        <div class="layout-main">
          <p>這是頁面的主要內容區域</p>
          <Card>嵌套的卡片組件</Card>
        </div>
      </template>
      <!-- 向footer插槽插入內容 -->
      <template #footer>
        <div class="layout-footer">
          <p>版權信息 © 2024</p>
        </div>
      </template>
    </Layout>
  </div>
</template>

<script setup>
import Layout from './Layout.vue'
import Card from './Card.vue'
</script>

具名插槽讓子組件的多個自定義區域各司其職,父組件可以按需填充每個插槽的內容,尤其適合佈局類組件(如頭部、主體、底部的拆分),讓組件結構更清晰。

還有一種常用場景:父組件需要使用子組件內部的數據來渲染插槽內容,這時作用域插槽就能派上用場。子組件通過<slot>標籤的屬性傳遞數據,父組件接收後,結合這些數據定製內容:

<!-- 子組件 List.vue -->
<template>
  <div class="list">
    <ul>
      <li v-for="(item, index) in list" :key="index">
        <!-- 向父組件傳遞當前項數據item和索引index -->
        <slot :item="item" :index="index"></slot>
      </li>
    </ul>
  </div>
</template>

<script setup>
import { reactive } from 'vue'

// 子組件內部的數據
const list = reactive([
  { name: 'Vue', type: '框架' },
  { name: 'React', type: '框架' },
  { name: 'Angular', type: '框架' }
])

// 定義插槽傳遞的屬性(可選,用於類型提示)
defineProps({})
defineEmits({})
</script>

<!-- 父組件 App.vue -->
<template>
  <div class="app">
    <h2>作用域插槽示例</h2>
    <!-- 接收子組件傳遞的插槽數據 -->
    <List>
      <template #default="slotProps">
        <!-- 父組件使用子組件傳遞的item和index -->
        <p>索引:{{ slotProps.index }} - 名稱:{{ slotProps.item.name }}({{ slotProps.item.type }})</p>
      </template>
    </List>

    <!-- 解構插槽數據,寫法更簡潔 -->
    <List>
      <template #default="{ item, index }">
        <p>第{{ index + 1 }}項:{{ item.name }} - 自定義顯示格式</p>
      </template>
    </List>
  </div>
</template>

<script setup>
import List from './List.vue'
</script>

作用域插槽的核心是 “數據反向傳遞”:子組件將內部數據通過插槽屬性暴露給父組件,父組件接收後(可通過解構簡化寫法),根據這些數據定製渲染邏輯。這種方式既複用了子組件的列表渲染邏輯,又讓父組件能靈活控制每一項的顯示格式,常見於表格、列表等需要自定義項渲染的組件。

此外,插槽還支持設置默認內容,當父組件沒有給插槽傳遞內容時,默認內容會生效,提升組件的可用性:

<!-- 子組件 Button.vue -->
<template>
  <button class="custom-btn">
    <!-- 默認內容:父組件未傳遞時顯示“默認按鈕” -->
    <slot>默認按鈕</slot>
  </button>
</template>

<!-- 父組件 App.vue -->
<template>
  <div class="app">
    <h2>插槽默認內容示例</h2>
    <!-- 未傳遞內容,顯示默認文本 -->
    <Button></Button>
    <!-- 傳遞自定義內容,替換默認值 -->
    <Button>提交表單</Button>
  </div>
</template>

slot 的設計讓 Vue 組件既保持了高複用性,又不失靈活性。默認插槽滿足簡單自定義需求,具名插槽解決多區域定製問題,作用域插槽實現數據聯動渲染,合理運用這三種插槽類型,能讓組件的通用性和擴展性大大提升,是 Vue 組件化開發中不可或缺的重要技巧。