遇到的問題?
在使用uni開發(fā)小程序或者App的時候,會遇到各種彈窗的需求,基本上都是一個黑色蒙版,上面中間有一塊區(qū)域可以顯示內(nèi)容,不同的彈窗,只有中間這一塊區(qū)域顯示的內(nèi)容是不同的,但是整個彈窗的展示和隱藏是可以抽取為一個組件的。為了解決這個需求,特此了解并學(xué)習(xí)記錄了插槽slot的使用。
Vue中的插槽slot?
- 是對組件的擴(kuò)展,通過slot插槽向組件內(nèi)部指定位置傳遞內(nèi)容(先占位,然后父組件去寫入內(nèi)容),通過slot可以父子傳參;
- 插槽顯示與否、顯示內(nèi)容是由父組件來控制,而插槽在哪顯示就由子組件來進(jìn)行控制
怎么使用插槽?
1. 默認(rèn)使用
父組件:
<template>
<div>
我是父組件
<slotOne1>
<p style="color:red">我是父組件插槽內(nèi)容</p>
</slotOne1>
</div>
</template>
子組件:slotOne1
<template>
<div class="slotOne1">
<div>我是slotOne1組件</div>
<slot><p>我是默認(rèn)顯示的內(nèi)容,父組件沒有設(shè)置內(nèi)容,那么我就會顯示</p></slot>
</div>
</template>

注意事項(xiàng):
- 在slot標(biāo)簽添加樣式無效
- 在父組件引用的子組件中也可以寫入其他組件
- 如果子組件中有多個slot標(biāo)簽,那么內(nèi)容也會被插入多次
2. 具名插槽
上面??注意事項(xiàng)提到過《如果子組件中有多個slot標(biāo)簽(沒有名字), 那么內(nèi)容會被插入多次》,那我們想使用多個插槽,并且每個插槽有不同的內(nèi)容,可以使用slot的一個attribute:name,其實(shí)一個不帶name的slot也會帶有隱含的名字‘default’。
在向具名插槽提供內(nèi)容的時候,我們可以使用v-slot指令,并以v-slot參數(shù)的形式提供其名稱。
父組件:
<template>
<child-page>
<text>第一次沒有指定名字的內(nèi)容</text>
<template v-slot:header>
<text style="display: block;">我是父組件給的標(biāo)題</text>
</template>
<template v-slot:footer>
<text style="display: block;">我是父組件第一次給的底部,不會顯示</text>
</template>
<text>|第二次沒有指定名字的內(nèi)容</text>
<template v-slot:footer>
<text style="display: block;">我是父組件第二次給的底部,會顯示</text>
</template>
</child-page>
</template>
子組件:
<template>
<view>
<text>這是child-page必顯示的標(biāo)題</text>
<slot name="header">
<text>header占位文本</text>
</slot>
<text style="display: block;">---我是中間的分割線---</text>
<slot></slot>
<slot name="footer"></slot>
</view>
</template>
- 具名插槽的縮寫
跟v-on和v-bind一樣,v-slot也有縮寫,就是把v-slot:替換為字符#, 例如v-slot:default為#default, v-slot: header為#header。
注意事項(xiàng):
- 父組件連續(xù)給指定
v-slot:footer賦值,后者會覆蓋前者,v-slot:default也是一樣 -
v-slot指令只能添加在<template>上(除了獨(dú)占默認(rèn)插槽的縮寫語法,下面會介紹到)
3. 作用域插槽
3.1 編譯作用域
這里要著重說一下編譯作用域:父級模板里的所有內(nèi)容都是在父級作用域中編譯的;子模板里的所有內(nèi)容都是在子作用域中編譯的。
比如:
<navigation-link url="/profile">
Logged in as {{ user.name }}
</navigation-link>
這個user.name肯定是在父組件的屬性,也就是說這個插槽跟模板的其他地方一樣可以訪問相同的實(shí)例property(也就是相同的作用域),而不能訪問<navigation-link>的作用域。
3.2 作用域插槽
有時候我們需要讓插槽內(nèi)容能夠訪問子組件中才有的數(shù)據(jù),例如,有一個帶有如下模板的<current-user>組件:
<span>
// 父組件不指定內(nèi)容,默認(rèn)會顯示 user.lastName
<slot>{{ user.lastName }}</slot>
</span>
我們在父組件中,可能想換掉備用內(nèi)容,用名非姓來顯示:
<current-user>
{{ user.firstName }}
</current-user>
當(dāng)然,這樣的代碼是不會正常工作的,因?yàn)橹挥?lt;current-user>組件可以訪問到user而我們提供的內(nèi)容是在父級渲染的,具體可以看?? 3.1的介紹。
為了讓user在父級的插槽內(nèi)容中可以訪問到,我們需要2步操作:
- 將
user作為<slot>元素的一個attribute綁定上去, 綁定在<slot>元素上的attribute被稱為插槽prop:
<span>
<slot v-bind:user="user">
{{ user.lastName }}
</slot>
</span>
- 在父級作用域中,我們可以使用帶值的v-slot來定義我們提供的插槽prop的名字:
<current-user>
<template v-slot:default="slotProps(這個是可以隨意命名的)">
{{ slotProps.user.firstName }}
</template>
</current-user>
3.3 獨(dú)占默認(rèn)插槽的縮寫語法
在上述情況下,當(dāng)被提供的內(nèi)容只有默認(rèn)插槽時,組件的標(biāo)簽才可以被當(dāng)做插槽的模板來使用,就是可以把v-slot直接用在組件上,可以跟上面對比一下代碼的不同:
<current-user v-slot:default="slotProps">
{{ slotProps.user.firstName }}
</current-user>
你認(rèn)為這就是最簡單了嗎?當(dāng)然不是。。。
<current-user v-slot="slotProps">
{{ slotProps.user.firstName }}
</current-user>
我們把default去掉,不帶參數(shù)的v-slot被假定對應(yīng)默認(rèn)插槽。
注意:默認(rèn)插槽的縮寫語法不能和具名插槽混用,因?yàn)樗鼤?dǎo)致作用域不明確,需要為所有的插槽使用完整的基于
<template>的語法.
錯誤?的代碼:
<!-- 無效,會導(dǎo)致警告 -->
<current-user v-slot="slotProps">
{{ slotProps.user.firstName }}
<template v-slot:other="otherSlotProps">
slotProps is NOT available here
</template>
</current-user>
正確?的代碼:
<current-user>
<template v-slot:default="slotProps">
{{ slotProps.user.firstName }}
</template>
<template v-slot:other="otherSlotProps">
...
</template>
</current-user>
3.4 解構(gòu)插槽Prop
首先,我們要明白一下:作用域插槽的內(nèi)部工作原理是將你的插槽內(nèi)容包括在一個擁有單個參數(shù)的函數(shù)里:
function (slotProps) {
// 這里是插槽內(nèi)容,我們可以直接使用slotProps
}
so,意味著v-slot的值實(shí)際上可以是任何能夠作為函數(shù)參數(shù)的JavaScript表達(dá)式。
I think : 這只是一個錦上添花的功能,可以讓模板更加簡潔。
大概了解一下吧,在支持的環(huán)境下(單文件組件或現(xiàn)代瀏覽器),可以使用ES2015解構(gòu)來傳入具體的插槽prop,如下:
- 讓模板更簡潔
<current-user v-slot="{ user }">
{{ user.firstName }}
</current-user>
- 將prop重命名
<current-user v-slot="{ user: person }">
{{ person.firstName }}
</current-user>
- 定義默認(rèn)顯示內(nèi)容
<current-user v-slot="{ user = { firstName: 'Guest' } }">
{{ user.firstName }}
</current-user>
4. 動態(tài)插槽名 2.6.0新增
動態(tài)指令參數(shù)也可以用在v-slot上,來定義動態(tài)的插槽名:
<base-layout>
<template v-slot:[dynamicSlotName]>
...
</template>
</base-layout>
5. 其他示例
插槽prop允許我們將插槽轉(zhuǎn)換為可復(fù)用的模板,這些模板可以基于輸入的prop渲染除不同的內(nèi)容。這在設(shè)計(jì)封裝數(shù)據(jù)邏輯同時允許父級組件自定義部分布局的可復(fù)用組件是最有用的。
例如,我們要實(shí)現(xiàn)一個 <todo-list> 組件,它是一個列表且包含布局和過濾邏輯:
<ul>
<li
v-for="todo in filteredTodos"
v-bind:key="todo.id"
>
{{ todo.text }}
</li>
</ul>
我們可以將每個todo作為父級組件的插槽,以此通過父級組件對其進(jìn)行控制,然后將todo作為一個插槽prop進(jìn)行綁定,這樣可以在不同的父級組件中,對todo數(shù)據(jù)進(jìn)行不同的展示:
<ul>
<li
v-for="todo in filteredTodos"
v-bind:key="todo.id"
>
<!--
我們?yōu)槊總€ todo 準(zhǔn)備了一個插槽,
將 `todo` 對象作為一個插槽的 prop 傳入。
-->
<slot name="todo" v-bind:todo="todo">
<!-- 后備內(nèi)容 -->
{{ todo.text }}
</slot>
</li>
</ul>
現(xiàn)在當(dāng)我們使用 <todo-list> 組件的時候,我們可以選擇為 todo 定義一個不一樣的template作為替代方案,并且可以從子組件獲取數(shù)據(jù):
<todo-list v-bind:todos="todos">
<template v-slot:todo="{ todo }">
<span v-if="todo.isComplete">?</span>
{{ todo.text }}
</template>
</todo-list>
只是為了學(xué)習(xí)&記錄&自己的一點(diǎn)理解,附上:
Vue.js官方文檔連接。