Vue 條件插槽 (Conditional Slots) 完全指南:原理、實作與排版優化
有時候您會需要依據內容是否被傳入插槽來渲染某些內容的技巧。例如:使用者沒有提供內容時,元件的某個區塊就完全不要顯示,甚至連包裝用的 HTML 標籤都消失。
條件插槽的核心原理
Vue 元件實體提供了一個 $slots 物件,它包含了所有父元件傳遞進來的插槽內容。如果父元件有傳入內容,會是一個陣列/函數;如果父元件沒傳入內容,會是 undefined。我們就利用這個特性配合 v-if 來達成條件渲染。
這與預設內容有什麼不同?
基本插槽 的預設內容只能決定 slot 那個位置 要顯示什麼,包在外面的標籤照樣會被渲染出來:
<!-- 父元件沒填 head,包裝標籤還在,padding 也還在 -->
<div class="card-head"></div>條件插槽處理的正是這個問題:沒有內容時,連外層標籤一起不渲染。
如何在 Vue 中實作條件插槽?
在下面的案例中,定義了一個卡片元件,內部擁有兩個插槽,分別為 head、body。當 head、body 的內容存在時,希望提供額外的樣式。
選項式 API (Options API)
在 <template> 中可以直接透過內建的 $slots 全域變數進行判斷。
<template>
<div class="card">
<div v-if="$slots.head" class="card-head">
<slot name="head"></slot>
</div>
<div v-if="$slots.body" class="card-body">
<slot name="body"></slot>
</div>
</div>
</template>組合式 API (Composition API)
如果使用的是 <script setup>,需要透過 useSlots 來存取插槽的狀態。
<script setup>
import { useSlots } from "vue";
const slots = useSlots();
</script>
<template>
<div class="card">
<div v-if="slots.head" class="card-head">
<slot name="head"></slot>
</div>
<div v-if="slots.body" class="card-body">
<slot name="body"></slot>
</div>
</div>
</template>在模板裡其實可以省略 useSlots
<script setup> 的模板同樣能直接使用 $slots,所以單純判斷有無內容時,寫 v-if="$slots.head" 就夠了。useSlots() 的價值在於 需要在邏輯中運用 的時候,例如:整理成 computed 供多處使用:
<script setup>
import { computed, useSlots } from "vue";
const slots = useSlots();
const hasFooter = computed(() => Boolean(slots.foot || slots.extra));
</script>判斷時容易誤判的地方
$slots 判斷的是 父元件有沒有提供這個插槽,而不是 它渲染出來有沒有東西:
<template>
<Card>
<!-- 判斷結果為真:template 存在,即使內容是空的 -->
<template #head></template>
<!-- 判斷結果為真:v-if 為 false 時仍然提供了插槽函式 -->
<template #body><p v-if="false">內容</p></template>
</Card>
</template>一般專案這樣就夠用了。若您在寫的是元件庫、需要更嚴謹,則要檢查插槽函式回傳的節點是否為空,成本較高,通常不建議一開始就這麼做。
為什麼應該使用條件插槽?
維持乾淨的 DOM 結構
傳統做法常會留下空的 <div></div>。使用 條件插槽 可以確保 HTML 結構與資料狀態完全同步,這對於 SEO 爬蟲理解頁面權重與結構非常有幫助。
精確的樣式控制 (解決 CSS Bug)
這是在排版上最重要的好處。許多元件的包裝層會有 padding、margin 或 border。如果內容為空卻渲染了標籤,這些樣式會導致 UI 出現異常的空白間距。
在使用 flex 或 grid 版面時問題更明顯:空標籤依然佔一個位置,gap 也照算,畫面會多出一段莫名的空隙。
提升渲染效能
減少瀏覽器需要處理的 DOM 節點數量,進而減輕樣式計算(Recalculate Style)與排版(Layout)的負擔。
常見問題
$slots 裡面裝的是什麼?
是一個以插槽名稱為 key 的物件,值為回傳虛擬節點的函式。父元件有傳入該插槽時才會有對應的 key,沒有傳入時取到的是 undefined,因此可以直接當條件判斷使用。
$slots 與 useSlots 差在哪裡?
兩者是同一份資料。$slots 可以在模板中直接使用,useSlots() 則是在 <script setup> 中取得它的方式,適合需要在邏輯或 computed 裡判斷的場合。
為什麼不直接用預設內容就好?
預設內容 只能決定 <slot> 位置要顯示什麼,無法移除包在外面的 HTML 標籤。當那個標籤帶有 padding、border 或 gap 時,即使內容為空也會留下多餘間距,這時就需要條件插槽。
$slots 的判斷結果會隨資料變動更新嗎?
會。$slots 是響應式的,父元件用 v-if 動態決定是否提供插槽內容時,子元件的判斷也會跟著重新計算。
父元件傳了空的 template 也算有內容嗎?
算。只要父元件寫了對應的 <template> 標籤,該插槽的 key 就存在,判斷結果為真。若要更嚴謹地排除實際渲染為空的情況,需要檢查該插槽函式回傳的節點內容。