slot(插槽)的概念是把外層的內容塞進子元件的指定位置裏。跟插槽的字面意思一樣,前提是:有插口才能插。子元件需要開一個插口(slot),才可以在外層元件把內容塞進子元件裏。
slot(插槽) 可分為四種:
以下會再作詳細解說。
先講解第一個最簡單使用 slot 的方法。
假設有一個叫 Card
的元件,我們想要從父層,把「夏日短裙」這文字塞進這個元件(即是子層),就可使用slot
:
結果:
以上就是 slot 概念:在外層把內容塞到子元件的指定位置裏。這就是預設插槽,也是最簡單的用法。
避免沒有傳入內容,可以設定顯示預值內容:
App.vue
<template>
<Card></Card>
</template>
Card.vue
<template>
<h1>
<slot>這是預設文字</slot>
</h1>
</template>
以上結果就會渲染「這是預設文字」。
借用這篇文章的解說例子,用下圖就能理解為什麼要使用具名插槽:
圖片來源:https://dev.to/samanthaming/how-i-m-using-vue-slots-on-my-site-nfn
假設有一個有很多工具的工具箱(父層內容),現在要把它們放到一個盒子裏的不同間隔範圍裏(子元件)。這時候,盒子裏要標示清楚這個範圍是放什麼工具。正如在子元件裏,要使用具名插槽來指定父層的內容要被指派塞進什麼地方。
例子:
結果:
注意:如果 slot 沒有命名,在父元件傳入內容時,需使用 v-slot:default
來標示。
另外,縮寫可以用 #
代替 v-slot:
:
<Card>
<template #title>
<h1> 夏日短裙 </h1>
</template>
<template #price>
<p> $200 </p>
</template>
<template #default>
<p> Sizes: XS, S, M, L </p>
</template>
</Card>
在進入介紹最後一個 slot 用法,scoped slot 之前。先理解為什麼需要用 slot。props 與 slot 的分別是:
<p> $200 </p>
這個 DOM 內容轉入子元件裏,這情況用 slot 就會方便很多。參考 Mike 老師這個影片,他有提到使用 slot 打造自己的 UI 元件。過程就如自行製作一個 Vue 模版,利用 named slot 預先在分配資料的位置。當父層傳入資料時,就自動把父層的資料,分配到子元件的不同位置裏。
舉例說,如果一個網頁的每個 section heading 都有同一個樣式,我可以把 heading 拆成一支獨立檔案。再同時利用 named slot 分配好不同資料的位置。這樣的好處是:
Heading.vue
<template>
<div class="section">
<h2>
<slot name="title"></slot>
</h2>
<p>
<slot name="slogan"></slot>
</p>
</div>
</template>
App.vue
<Heading v-for="section in sections" :key="section.title">
<template #title>
{{ section.title }}
</template>
<template #slogan>
{{ section.slogan }}
</template>
</Heading>
export default {
components: {
Heading,
},
data() {
return {
sections: [
{
title: "限時優惠",
slogan: "搶購限時最便宜貨品!",
},
{
title: "最新商品",
slogan: "為你帶來最新貨品!",
},
{
title: "一般商品",
slogan: "多種商品任你選擇!",
},
],
};
},
};
結果:
Scoped slot 是指把子元件的資料取出來,給父層使用。這是因為父層無法取得子元件的資料。
舉例說,每個商品都用一個 Card 元件來呈現。商品資料在父層,而 Card 的資料呈現和樣式就在 Card 元件裏面。像以下結構:
App.vue
<Card>
<template #title>...</template>
<template #price>...</template>
<template #size>...</template>
</Card>
export default {
components: {
Card,
},
data() {
return {
items: [
{
title: "夏日短裙",
price: 200,
sizes: ["XS", "S", "M", "L"],
},
{
title: "純白 T-shirt",
price: 100,
sizes: ["XS", "S"],
},
{
title: "針織背心",
price: 300,
sizes: ["XS", "S", "M"],
},
],
};
},
};
components/Card.vue
<div class="card">
<h1 class="title">
<slot name="title"></slot>
</h1>
<p>
售價:
<slot name="price"></slot>
</p>
<p>
<slot name="size"></slot>
</p>
<div class="quantity">
<label for="quantity">數量:</label>
<select name="quantity" id="quantity" v-model="quantity">
<option value="0">0</option>
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
</div>
<p class="result">
<slot name="result" :quantity="quantity" ></slot>
</p>
</div>
export default {
data() {
return {
quantity: 0
}
}
};
結果每個 Card 都會長這樣:
在這情況中,數量的 select 在子元件(Card.vue)裏。可是,父層需要利用子元件 select 的結果,組成:已選購:{{ item.title }} x {{ quantity}}
這字串,再塞回子元件的 <slot name="result"></slot>
裏面。
解決方法:
在子元件(Card.vue) 的 slot 裏綁定 prop(我們稱為 slot props),然後在父層的相應 template
裏取出來用,並且組字串,塞回子元件裏。
父層 App.vue
<template #result="slotProps">
已選購:{{ item.title }} x{{ slotProps.quantity }}
</template>
子元件 Card.vue
<p class="result">
<slot name="result" :quantity="quantity"></slot>
</p>
注意,props 名稱不一定叫 slotProps,可自定義其他名稱。再簡潔一點,使用解構寫法:
<template #result="{ quantity }">
已選購:{{ item.title }} x{{ quantity }}
</template>
https://codesandbox.io/s/scoped-slot-yong-fa-dxqh4?file=/src/App.vue:320-416
Vuetify是一個提供給Vue開發者的material UI 框架。
它有大量使用Scoped slot,讓開發者客製化Vuetify裏封裝好的元件,以 table 這元件為例。這裏有使用scoped slot,指定要更改Calories這個item裏的內容:
動態插槽很簡單,就是在父層可以動態指定不同 slot,把內容塞到子元件裏。
父層,使用中括號動態指定 slot:
<template>
<div v-for="area in areas" :key="area">
<input type="radio" :id="area" :value="area" v-model="chosenArea">
<label :for="area"> {{ area }} </label>
</div>
<Color>
<template v-slot:[chosenArea]>
<span>我現在在: {{ chosenArea }}</span>
</template>
</Color>
</template>
data() {
return {
chosenArea: 'nav',
areas: ['nav', 'main', 'footer']
}
}
子層,動態建立slot:
<p v-for="area in areas" :key="area" :class="area">
<slot :name="area"></slot>
</p>
export default {
data() {
return {
areas: ['nav', 'main', 'footer']
}
}
}
<style>
.nav {
color: red;
}
.main {
color: black;
}
.footer {
color: blue;
}
</style>
https://codesandbox.io/s/dynamic-slot-shi-fan-4yenc
Vue.js: slots vs. props
How I'm using Vue Slots on my site
The Difference Between Props, Slots and Scoped Slots in Vue.js
重新認識 Vue.js - 2-4 編譯作用域與 Slot 插槽
Vue slots 使用超入門
複用元件的好幫手:Vue Slots(v-slot、Scoped Slots)