Slots 插槽
說明
「父組件」不只能傳送資料 (props
) 給「子組件」,也可以將「渲染內容」傳送過去,讓 「子組件」 透過 <slot />
在自已模板中渲染這個傳送的「渲染內容」。
在組件中設置的 <slot />
會提供了一個插槽,會為「父組件」將來傳送的「渲染內容」預留插入的位置。
Demo
🟢 基本 slot 操作
1️⃣ 子組件 => 設置 <slot />
插槽
html
<template>
<button>
<!-- 預留插槽 -->
<slot />
</button>
</template>
2️⃣ 父組件 => 傳送「元件」到「子組件」
html
<SlotChirldren>
<!-- 替換 slot -->
Click Me!
</SlotChirldren>
3️⃣ 渲染後
html
<button>
Click Me!
</button>
TIP
- 若「子組件」無設置
slot
,「父組件」傳送「渲染內容」,直接被丟棄。 - 若「子組件」有多個 不具名
slot
,「父組件」傳送 單一渲染內容 (不具名),會同時顯示在多個 不具名 插槽。 - 若「父組件」有多個
不具名
內容傳送,會被 合拼 渲染到子組件slot
。
渲染作用域
「子組件」 僅負責 組件外層 的編譯,而 slot
的內容是由提供傳送的「父組件」負責編譯,所以 slot
的「渲染內容」,是可以訪問 「父組件」的數據。
Demo
子組件
html
<button class="submit-btn">
<slot />
</button>
父組件
html
<SlotChirldren>
<span style="color: red">
{{ childrenBtnContent }}
</span>
</SlotChirldren>
js
export default {
data: () => ({
childrenBtnContent: 'Click Me!'
})
}
渲染後
html
<button class="submit-btn">
<span style="color: red;">Click Me!</span>
</button>
默認內容
當在 slot
設置默認內容,若「父組件」無傳入「渲染內容」,就會顯示「默認內容」。
TIP
常使用在無資料顯示,當沒有傳入資料時,顯示「默認」的設置內容。
設置默認內容
子組件
html
<button class="submit-btn">
<slot>Disable</slot>
</button>
「父組件」無傳入 slot
內容
js
<SlotChirldren />
渲染後,顯示默認內容
html
<button class="submit-btn">
Disable
</button>
「父組件」傳入 slot
內容
js
<SlotChirldren>
Click Me!
</SlotChirldren>
渲染後
html
<button class="submit-btn">
Click Me!
</button>
🟢 具名 slot 操作
當「子組件」有多個 slot
插槽時,具名的插槽就會非常有用,它可以指定插入的位置。 圖片出處
子組件
html
<template>
<div>
<h5>Chirldren component</h5>
<header>
<span>Header: </span>
<!-- 具名插槽 -->
<slot name="header" />
</header>
<button class="submit-btn">
<slot> disable!</slot>
</button>
<main>
Main:
<slot name="main" />
</main>
<footer>
<!-- 具名插槽 -->
<slot name="footer" />
</footer>
</div>
</template>
父組件傳送方式
1️⃣ template 方式
v-slot:插槽名稱
/#插槽名稱
html<SlotChirldren> <template v-slot:header> <!-- 傳送插槽 name="header" --> 由父層傳送 header 內容 </template> <template #footer> <!-- 傳送插槽 name="footer" --> 由父層傳送 footer 內容 </template> <h3 slot="main">父層傳送 Main 內容!</h3> Click Me! </SlotChirldren>
WARNING
這種寫法,必須是
<template />
標籤。2️⃣ 任意標籤方式
<tag slot="插槽名稱">渲染內容</tag>
html<SlotChirldren> <h3 slot="main">父層傳送 Main 內容!</h3> </SlotChirldren>
渲染後
html
<div>
<h5>Chirldren component</h5>
<header>
<span>Header: </span>
<!-- 具名插槽 -->
由父層傳送 header 內容
</header>
<button class="submit-btn">
<!-- 不具名插槽 -->
Click Me!
</button>
<main>
Main:
<h3>父層傳送 Main 內容!</h3>
</main>
<footer>
<!-- 具名插槽 -->
由父層傳送 footer 內容
</footer>
</div>
🟢 動態具名 slot 插槽
- template 方式
v-slot:[動態參數]
(簡寫#[dynamicSlotName]
) - 任意標籤方式
:slot="動態參數"
子組件
html
<template>
<div>
<div>
Dynamic:
<slot name="dynamic" />
</div>
</div>
</template>
父組件
html
<SlotChirldren>
<template v-slot:[dynamicSlotName] style="color: lightblue">
<span style="color: lightblue">Parent Pass to Dynamic Content</span>
</template>
<!-- 簡寫 -->
<template #[dynamicSlotName] style="color: lightblue">
<span style="color: lightblue">Parent Pass to Dynamic Content</span>
</template>
<!-- 非 template 寫法 -->
<span
:slot="dynamicSlotName"
style="color: lightblue"
>
Parent Pass to Dynamic Content
</span>
</SlotChirldren>
🟢 slot 資料向上傳遞 (Scoped Slots)
「渲染內容」只可以訪問到「父組件」的數據 ( 渲染作用域 ),但若需要取得「子組件」數據的話,可以這樣做:
- 「子組件」設置動態參數傳遞屬性
<slot :傳遞到父層參數名稱="子組件資料" />
- 「父組件」設置
v-slot="父組件自定義參數名"
來接收數據,「子組件」的傳遞到父層參數名稱
會在父組件自定義參數名
之下。
基本範例
子組件
html
<template>
<div>
<h1>Slot Scope</h1>
<slot :num="123" text="slot" />
</div>
</template>
父組件
html
<SlotScope v-slot="slotData">
{{ slotData.text }} {{ slotData.num }}
<!-- slot 123 -->
</SlotScope>
<!-- 解構寫法 -->
<SlotScope v-slot="{ text, num }">
{{ text + num }}
<!-- slot123 -->
</SlotScope>
注意
<SlotScope v-slot="slotData">
這個方法只適用在沒有「具名」的 slot
傳參。
只要內容有 「具名」 渲染內容,就會無法被編譯,這是為了避免默認插槽傳送參數與「具名」插槽參數衝突。
html
<!-- ⛔⛔⛔ 無法編譯 ⛔⛔⛔ -->
<SlotScope v-slot="{ num, text }">
{{ text + num }}
<template #header>Header: </template>
</SlotScope>
具名 slot 傳參方法 (使用 template
標籤)
「父組件」使用 template
標籤,可以簡單明白的接收傳遞的傳數。 為了避免「默認」與「具名」的參數資料衝突,建議不直接寫 v-slot
在子組件 SlotScope
上,而是直接寫在插槽「渲染內容」上。
子組件
html
<div>
<h1>Slot Scope</h1>
<slot :num="123" text="slot" />
<slot name="header" headerProps="header 傳送的內容" />
</div>
父組件
- 使用
v-slot="{ 默認 slot 參數名稱 }"
接「默認」slot
傳送的資料。 - 使用
#具名插槽名稱="{ 具名 slot 參數名稱 }"
接「具名」slot
傳送的資料。
html
<!-- es6 解構 -->
<SlotScope>
<!-- 簡寫 v-slot="{ 默認 slot 參數名稱 }" -->
<template #default="{ num, text }">{{ text + num }}</template>
<template #header="{ headerProps }">Header: {{ headerProps }}</template>
</SlotScope>
具名 slot 傳參方法 (使用任意標籤)
不使用 template
的情況下,在「渲染內容」可以使用 slot-scope="{ 子層具名slot參數名稱 }"
,來接收「子組件」傳遞的參數資料。
子組件
html
<div style="border: 1px solid lightgreen; border-radius: 20px">
<h1>Slot Scope</h1>
<slot :num="123" text="slot" />
<slot name="header" headerProps="header 傳送的內容" />
<slot name="main" mainProps="main 傳送的內容" />
</div>
父組件
html
<SlotScope>
<template #default="{ num, text }">{{ text + num }}</template>
<h1 slot="main" slot-scope="{ mainProps }">{{ mainProps }}</h1>
</SlotScope>
進階運用
在「父組件」將請求數據的方法傳遞 props
給「子組件」,「子組件」請求且取得數據資料後,向上傳遞由「父組件」寫入「渲染內容」。
子組件
html
<ul>
<li v-for="item in items">
<slot name="item" v-bind="item"></slot>
</li>
</ul>
父組件
html
<FancyList :api-url="url" :per-page="10">
<template #item="{ body, username, likes }">
<div class="item">
<p>{{ body }}</p>
<p>by {{ username }} | {{ likes }} likes</p>
</div>
</template>
</FancyList>