(1)插槽內容
Vue 實現了一套內容分發的 API,這套 API 的設計靈感源自 Web Components 規范草案,將 <slot>
元素作為承載分發內容的出口。
在父級組件里可以這樣寫
<div class="slot_area"> <navigation-link v-bind:url="url"> Your Profile你的個人簡介資料 </navigation-link> </div>
然后在 <navigation-link>
組件模板中:
Vue.component('navigation-link',{ props:['url'], template:`<a v-bind:href="url" class="nav-link"> <span class="slot_span"></span> <slot></slot> </a>` });
new Vue({
el:'.slot_area',
data:{
url:'http://www.baidu.com'
}
})
當組件渲染的時候,<slot></slot>
將會被替換為“Your Profile你的個人簡介資料”。
插槽內可以包含任何模板代碼,包括 HTML:
Vue.component('navigation-link',{
props:['url'],
template:`<a v-bind:href="url" class="nav-link">
<!-- 添加一個 Font Awesome 圖標 -->
<span class="fa fa-user"></span>
<slot></slot>
</a>`
});
插槽也可以是其他組件
<div class="slot_area"> <navigation-link v-bind:url="url"> Your Profile你的個人資料簡介 <slot-component></slot-component> </navigation-link> </div> <script type="text/javascript"> Vue.component('navigation-link',{ props:['url'], template:`<a v-bind:href="url" class="nav-link"> <!-- 添加一個 Font Awesome 圖標 --> <span class="slot_span"></span> <slot></slot> </a>` }); Vue.component('slot-component',{ template:'<h4>插槽可以是其他模板</h4>' }) new Vue({ el:'.slot_area', data:{ url:'http://www.baidu.com' } }) </script>
結果:
注意:如果 <navigation-link>
沒有包含一個 <slot>
元素,則該組件起始標簽和結束標簽之間的任何內容都會被拋棄。即如果模板中沒有slot插槽元素,則在父級組件里加的內容都會被拋棄。
(2)編譯作用域
父組件模板內容是在父組件作用域中編譯的,子組件模板內容是在子組件里編譯的。
先來個簡單的案例:
<parent-scrop>
{{message}}
</parent-scrop>
Vue.component('parent-scrop',{ template:`<div> 子組件模板內容 <slot></slot> </div>` }); new Vue({ el:".parent_scrop", data:{ message:"父組件模板內容" } });
這里的message就是一個slot,但是其綁定的是父組件的數據,而不是子組件模板的數據。
再來看個案例:
<div class="parent_scrop"> <parent-scrop v-show="showStatus"></parent-scrop> </div> Vue.component('parent-scrop',{ template:`<div> 子組件模板內容 </div>` }); new Vue({ el:".parent_scrop", data:{ showStatus:true } })
這里的showStatus綁定的是父級組件的數據,如果想在子級組件上綁定,應該是
<div class="parent_scrop"> <parent-scrop></parent-scrop> </div> Vue.component('parent-scrop',{ template:`<div v-show="showStatus"> 子組件模板內容 </div>`, data:function(){ return { showStatus:false } } }); new Vue({ el:".parent_scrop" })
結果:
因此,slot分發的內容,作用域是在父組件上的。
(3)slot用法
①單個slot
在子組件內使用特殊的<slot>元素就可以為這個子組件開啟一個slot(插槽),在父組件模板里,插入在父組件標簽內的所有內容都將替代子組件的<slot>標簽及它的內容。
demo1:
<div class="single_slot"> <single-slot></single-slot> </div> Vue.component('single-slot',{ template:` <div> <slot> <p>如果父組件沒有插入內容,將會作為默認值出現</p> </slot> </div> ` }); new Vue({ el:".single_slot" })
結果:
demo2:父組件插入內容
<div class="single_slot"> <single-slot> <p>父組件插入內容</p> <p>分發更多內容</p> </single-slot> </div> Vue.component('single-slot',{ template:` <div> <slot> <p>如果父組件沒有插入內容,將會作為默認值出現</p> </slot> </div> ` }); new Vue({ el:".single_slot" })
結果:
(4)后備內容/備用內容
結合上例,<single-slot>模板里定義了<slot>元素,並用了<p>作為默認內容。在父組件沒有使用slot即未插入內容時,會渲染這段默認文本;如果寫入了slot,那么就會替換整個<slot>。
注意:子組件<slot>中的后備內容,其作用域是子組件本身。
(5)具名插槽
給<slot>元素指定一個name后,可以分發多個內容,具名插槽可以與單個插槽共存。
用法簡單demo1:
父級組件里: <solt-area> <h2 slot="header">標題</h2> <p slot="footer">底部</p> </slot-area> 子級模板里: Vue.component('slot-area',{ template:` <div class="main_area"> <div class="header"><slot name="header"></slot></div> <div class="footer"><slot name="footer"></slot></div> </div> ` })
demo2:
<!-- 具名slot --> <div class="slot_name"> <slot-name> <h2 slot="header">文章標題</h2> <p>內容展示區域1,文章具體內容1... ...</p> <p>內容展示區域2,文章具體內容2... ...</p> <p slot="footer">作者:Tony 發布日期:2020.6.8</p> </slot-name> </div> <style type="text/css"> .main_area{ padding: 20px; background-color: rgba(0,0,0,0.1); } .main_area h2{ margin: 10px 0; text-align: center; } .main_area .footer{ text-align: right; font-weight: 600; margin: 10px 0; } .main_area .main{ padding: 10px; background-color: rgba(0,0,0,.1); } </style> /* 3、具名slot */ Vue.component('slot-name',{ template:` <div class="main_area"> <div class="header"><slot name="header"></slot></div> <div class="main"><slot></slot></div> <div class="footer"><slot name="footer"></slot></div> </div> ` }) new Vue({ el:".slot_name" })
結果:
子組件里聲明了3個<slot>元素,其中在<div class="main"></div>內的slot沒有使用name特性,所以他將作為默認slot出現,父組件里沒有使用slot特性的元素與內容都將出現在這里。
如果沒有指定默認的匿名slot,父組件里多余的內容片段都將會被拋棄。在組合使用組件時,內容分發API至關重要。
注意:自2.6.0開始有所跟新,將原先的slot方法更新為v-slot
指令即
<div class="slot_name"> <slot-name> <template v-slot:header> <h2>文章標題</h2> </template> <template v-slot:default> <p>內容展示區域1,文章具體內容1... ...</p> <p>內容展示區域2,文章具體內容2... ...</p> </template> <template v-slot:footer> <p>作者:Tony 發布日期:2020.6.8</p> </template> </slot-name> </div>
現在 <template>
元素中的所有內容都將會被傳入相應的插槽。任何沒有被包裹在帶有 v-slot
的 <template>
中的內容都會被視為默認插槽的內容。然而,如果你希望更明確一些,仍然可以在一個 <template>
中包裹默認插槽的內容,如上所示v-slot:default。
(6)作用域插槽
正常情況下,父級組件的插槽內容無法訪問子級模板中的數據,例如
<div class="scrop_slot"> <scrop-slot> 姓:{{user.firstName}},名:{{user.lastName}} </scrop-slot> </div> /* 4、作用域插槽 */ Vue.component('scrop-slot',{ template:` <div class="show_area"> <slot> <p>姓:{{user.firstName}},名:{{user.lastName}}</p> </slot> </div> `, data:function(){ return { user:{firstName:"鄧",lastName:"超"} }; } }) new Vue({ el:".scrop_slot" }) /* 4、作用域插槽 */
此時,控制台會出現報錯提示
為了讓 user
在父級的插槽內容可用,我們可以將 user
作為一個 <slot>
元素的特性綁定上去:
<slot v-bind:user="user">
<p>姓:{{user.firstName}},名:{{user.lastName}}</p>
</slot>
綁定在 <slot>
元素上的特性被稱為插槽 prop。現在在父級作用域中,我們可以給 v-slot
帶一個值來定義我們提供的插槽 prop 的名字:
<div class="scrop_slot"> <scrop-slot v-slot:default="slotDefault"> <template> 姓:{{slotDefault.user.firstName}},名:{{slotDefault.user.lastName}} </template> </scrop-slot> </div>
在這個例子中,選擇將包含所有插槽 prop 的對象命名為 slotProps
,也可以使用任意你喜歡的名字。
(7)獨占默認插槽的縮寫語法
<div class="scrop_slot"> <scrop-slot v-slot:default="slotDefault"> <template> 姓:{{slotDefault.user.firstName}},名:{{slotDefault.user.lastName}} </template> </scrop-slot> </div>
在上述情況下,當被提供的內容只有默認插槽時,組件的標簽才可以被當作插槽的模板來使用。這樣我們就可以把 v-slot
直接用在組件上:
<div class="scrop_slot"> <scrop-slot v-slot:default="slotDefault"> 姓:{{slotDefault.user.firstName}},名:{{slotDefault.user.lastName}} </scrop-slot> </div>
這種寫法還可以更簡單。就像假定未指明的內容對應默認插槽一樣,不帶參數但 v-slot
被假定對應默認插槽:
<div class="scrop_slot"> <scrop-slot v-slot="slotDefault"> 姓:{{slotDefault.user.firstName}},名:{{slotDefault.user.lastName}} </scrop-slot> </div>
注意:注意默認插槽的縮寫語法不能和具名插槽混用,因為它會導致作用域不明確。如下所示
<scrop-slot v-slot="slotDefault"> 姓:{{slotDefault.user.firstName}} <template v-slot:other="otherSlot"> 名:{{slotDefault.user.lastName}} </template> </scrop-slot>
此時,控制台出現報錯:譯為---為避免范圍模糊,當存在其他命名槽時,默認槽也應使用<template>語法。
<scrop-slot> <template v-slot:datault="slotDefault"> 姓:{{slotDefault.user.firstName}} </template> <template v-slot:other="otherSlot"> 名:{{otherSlot.user.lastName}} </template> </scrop-slot>
只要出現多個插槽,請始終為所有的插槽使用完整的基於 <template>
的語法。
(8)解構插槽Prop
可以使用 ES2015 解構來傳入具體的插槽 prop,如下:
<div class="scrop_slot"> <scrop-slot v-slot="{user}"> {{user.firstName}} </scrop-slot> </div> Vue.component('scrop-slot',{ template:` <div class="show_area"> <slot v-bind:user="user"> <p>姓:{{user.firstName}},名:{{user.lastName}}</p> </slot> </div> `, data:function(){ return { user:{firstName:"鄧",lastName:"超"} }; } }) new Vue({ el:".scrop_slot" })
這樣可以使模板更簡潔,尤其是在該插槽提供了多個 prop 的時候。它同樣開啟了 prop 重命名等其它可能,例如將 user
重命名為 person
:
<scrop-slot v-slot="{user:person}"> {{person.firstName}} </scrop-slot>
(9)動態插槽名(待驗證)
(10)具名插槽縮寫
跟 v-on
和 v-bind
一樣,v-slot
也有縮寫,即把參數之前的所有內容 (v-slot:
) 替換為字符 #
。例如 v-slot:header
可以被重寫為 #header
:
<div class="slot_name"> <slot-name> <template #header> <h2>文章標題</h2> </template> <template #default> <p>內容展示區域1,文章具體內容1... ...</p> <p>內容展示區域2,文章具體內容2... ...</p> </template> <template #footer> <p>作者:Tony 發布日期:2020.6.8</p> </template> </slot-name> </div>
.