用途
將每個功能都包成component時,很方便可以直接拿來重複使用。
但有時總會有一些想要一些不同,總不能再額外寫一個很像的component,這樣就失去了component的重複使用性了。
這時可以透過<slot>標籤,component裡面放一些自定義的文字或內容。
slot標籤可以想像成是在component裡面挖了一個洞,將你要的內容塞進去。
這邊有兩種挖洞的方式,分別是單個插槽(Single Slot)與具名插槽(Named Slots)。
先來看一個沒有使用slot的範例
HTML部分:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| <h2>沒有插槽可替換的狀態</h2> <no-slot-component> <p>我會被替換掉</p> </no-slot-component>
<script type="text/x-template" id="noSlotComponent"> <div class="alert alert-warning"> <h6>我是一個元件</h6> <p> 這沒有插槽。 </p> </div> </script>
|
Javascript部分:
1 2 3
| Vue.component('no-slot-component', { template: '#noSlotComponent', });
|
結果如圖,只有呈現template裡面原本的內容 “這沒有插槽”。
而”替換的內容”卻沒有出現。

這是為什麼呢?
還記得生命週期中有個template的橋段
如果有template就會執行template 沒有就innerHTML
因此,當宣告component時有指定template的話,
在使用show me的時候,瀏覽器會去讀取指定template裡的內容,所以說直接寫在component中間的東西是不會被呈現的。
但有個例外,那就是使用slot 標籤。
單個插槽(Single Slot)
在template中加入<slot></slot>。
HTML部分:
1 2 3 4 5 6 7 8 9 10 11 12
| <h2>Slot 基礎範例</h2> <h3>以外部資料取代slot</h3> <single-slot-component> <p>使用這段取代原本的 Slot。</p> </single-slot-component>
<script type="text/x-template" id="singleSlotComponent"> <div class="alert alert-warning"> <h6>我是一個元件</h6> <slot>如果沒有內容,則會顯示此段落。</slot> </div> </script>
|
Javascript部分:
1 2 3
| Vue.component('single-slot-component', { template: '#singleSlotComponent', });
|
結果如圖,應該出現”如果沒有內容,則會顯示此段落。”
被取代為”使用這段取代原本的 Slot。”

在上面的範例如果在component裡沒有放其他內容,如下:
HTML部分:
1 2 3
| <h3>使用compoment slot裡default資料</h3> <single-slot-component> </single-slot-component>
|
結果如圖,會直接採用component裡slot的default資料

具名插槽(Named Slots)
這時問題來了,如果想要在一個component裡挖很多洞放很多個slot呢?
這時就會需要幫每一個slot命名。
HTML部分:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| <h2>具名插槽</h2> <named-slot-component> <header slot="header">替換的 Header</header> <template slot="footer">替換的 Footer</template> <template slot="btn">按鈕內容</template> <p slot="content">其餘的內容</p> </named-slot-component>
<script type="text/x-template" id="namedSlotComponent"> <div class="card my-3"> <div class="card-header"> <slot name="header">這段是預設的文字</slot> </div> <div class="card-body"> <slot name="content"> <h5 class="card-title">Special title treatment</h5> <p class="card-text">With supporting text below as a natural lead-in to additional content.</p> </slot> <a href="#" class="btn btn-primary"> <slot name="btn">spanGo somewhere</slot> </a> </div> <div class="card-footer"> <slot name="footer">這是預設的 Footer</slot> </div> </div> </script>
|
Javascript部分:
1 2 3
| Vue.component('named-slot-component', { template: '#namedSlotComponent', });
|
結果如圖,內容有順利被替換掉。

不過可以注意到的是,當有name的時候,畫面顯示的順序會依照
template內所定義的位置呈現,也就是說在引用component時裡面寫的順序調換不受影響,如下:
HTML部分:
1 2 3 4 5 6 7
| <h2>具名插槽</h2> <named-slot-component> <template slot="footer">替換的 Footer</template> <template slot="btn">按鈕內容</template> <p slot="content">其餘的內容</p> <header slot="header">替換的 Header</header> </named-slot-component>
|
以上範例的完整程式碼
v-slot寫法
在vue2.6版本時slot出了新的寫法v-slot
我這邊試寫了一下,範例可以參考v-slot寫法的程式碼
裡面有幾個點需要注意一下
- v-slot 只能添加到
<template> 或自定義的component上,像是<div>或<p>就會錯誤。舊的寫法就沒有這個限制。
- v-slot的缩写是#
- 但建議若要使用v-slot就從頭到尾都用v-slot,若要用#就從頭到尾都用#
- 如果要使用
v-slot要確認使用的vue的版本是否有支援,版本需2.6以上
編譯作用域(Compilation Scope)
編譯作用域是指編譯層級以所在範圍為主,也就是說父層的data由父層的data決定,子層的data由子層的data決定。
這邊用來兩個例子來說明
第一個範例:沒有使用slot
範例完整程式碼
HTML部分
1 2 3 4 5 6 7 8 9
| <div id="app"> <div class="parent">{{ msg }}</div> <child>{{ msg }}</child> </div>
<template id="child-template"> <div class="child">{{ msg }}</div> </template>
|
Javascript部分
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| Vue.component('child', { template: '#child-template', data: function () { return { msg: 'I\'m child.' }; } });
var parent = new Vue({ el: '#app', data: { msg: 'I\'m parent.' } });
|
畫面呈現如下:

會這樣呈現的原因有兩個
- 剛剛提到生命週期的部分,在沒有使用
slot的狀況下會直接讀取template裡的內容當模板渲染,因此<child></child>會被無視,只呈現<div class="child"></div>的內容
- 由於編譯作用域的原因,會讀取所在的component的data,所以
<div class="child"></div>裡的msg是讀取子component的data
第二個範例:有使用slot
範例完整程式碼
HTML部分
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| <div id="app"> <div class="parent">{{ msg }}</div>
<child> <div>This "msg" is from parent: {{ msg }}</div> </child> </div>
<template id="child-template"> <div class="child"> <div>{{ msg }}</div> <slot> Default Slot Content </slot> </div> </template>
|
畫面呈現如下:

會這樣呈現的原因
- 根據編譯作用域的原因,template中的第一個msg,會讀取子component的data
- 因為有使用
slot,所以<slot><slot>中會呈現<div>This "msg" is from parent: </div>,由於<div>This "msg" is from parent: </div>所在位置為根實體,所以會取得根實體data中的msg呈現
參考資料