玩轉(zhuǎn)vue的slot內(nèi)容分發(fā)
vue的內(nèi)容分發(fā)非常適合“固定部分+動(dòng)態(tài)部分”的組件的場(chǎng)景,固定部分可以是結(jié)構(gòu)固定,也可以是邏輯固定,比如下拉loading,下拉loading只是中間內(nèi)容是動(dòng)態(tài)的,而拉到底部都會(huì)觸發(fā)拉取更多內(nèi)容的操作,因此我們可以把下拉loading做成一個(gè)有slot的插件。
單個(gè)Slot
在children這個(gè)標(biāo)簽里面放Dom,Vue不會(huì)理你,也就是不會(huì)顯示,類似React:this.props.children。
//父
<children>
<span>12345</span>//這邊不會(huì)顯示
</children>
//子
components: {
children: {
template: "<button>為了明確作用范圍,所以使用button標(biāo)簽</button>"
}
}
你需要寫成這樣
children: {
template: "<button><slot></slot>為了明確作用范圍,所以使用button標(biāo)簽</button>"
}
注意這邊 slot 相當(dāng)于一個(gè)坑,等著父組件給填上,這邊 slot 代表的就是上面的 span
多個(gè)Slot
這邊需要加name屬性,說(shuō)白了,多個(gè)Slot就不像上面單個(gè),需要有個(gè)對(duì)應(yīng)關(guān)系。
父-> slot="name1"
子-> <slot name="name1"
//父
<children>
<span slot="name1">12345</span>
</children>
//子
components: {
children: {
template: "<button>
<slot name="name1"></slot>
button標(biāo)簽
</button>"
}
}
這邊寫了一個(gè)name1,如果有多個(gè),就插多個(gè),比較簡(jiǎn)單。
使用場(chǎng)景
“下拉加載更多”的場(chǎng)景在移動(dòng)端相對(duì)來(lái)說(shuō)出現(xiàn)得比較多。我們知道下拉觸底都要監(jiān)聽(tīng)觸底事件,觸底的操作也相同(去后臺(tái)拉取數(shù)據(jù)),分頁(yè)算法也相同,因此我們會(huì)想到把它做成一個(gè)組件,重用這些相同的地方,讓其他地方可以共用這個(gè)組件,從而減少代碼量。
然而,下拉loading并不是一個(gè)可以完全重用的組件,因?yàn)榱斜砝锩娴膬?nèi)容不同,空白頁(yè)(沒(méi)有內(nèi)容時(shí))的內(nèi)容也可能不同,如果要做成組件,那么就要考慮到這方面的“不同”,因此我們想到利用vue的內(nèi)容分發(fā)slot來(lái)做。下面是本人在開(kāi)發(fā)的時(shí)候做的一個(gè)下拉loading,大家可以參考下。
組件代碼:
<template>
<div>
<slot name="list" v-if="total > 0"></slot>
<slot name="empty" v-else></slot>
</div>
</template>
<script>
import Toast from 'lib/xl-toast'
import Tool from 'tool/tool'
export default {
data() {
return {
page: 1,
isLoading: false,
busy: false,
isFirstLoad: false
}
},
props: {
pageSize: {
default: 10 // 每頁(yè)展示多少條數(shù)據(jù)
},
total: {
default: 0 // 總共多少條記錄
}
},
computed: {
totalPage() {
return Math.ceil(this.total / this.pageSize)
}
},
created() {
this.getList()
},
mounted() {
this.addScrollListener()
},
methods: {
addScrollListener() {
// 添加監(jiān)聽(tīng)滾動(dòng)操作,用到函數(shù)防抖
this.scrollFn = Tool.throttle(this.onScroll, 30, 30)
document.addEventListener('scroll', this.scrollFn, false)
},
getList() {
// 正在拉取數(shù)據(jù)或者沒(méi)有數(shù)據(jù)了,則取消滾動(dòng)監(jiān)聽(tīng)
if(this.isLoading || this.isFirstLoad && (this.page > this.totalPage)) {
document.removeEventListener('scroll', this.scrollFn, false)
return
}
this.busy = true
this.isLoading = true
// 通知父組件去拉取更多數(shù)據(jù)
this.$emit("getList", this.page, () => {
this.isFirstLoad = true
this.isLoading = false
this.page++
}, () => {
Toast.show('網(wǎng)絡(luò)錯(cuò)誤,請(qǐng)稍后重試')
this.total = 0
this.isLoading = false
})
},
reset() {
// 重新拉取數(shù)據(jù)
this.page = 1
this.total = 0
this.isLoading = false
this.isFirstLoad = false
this.addScrollListener()
this.getList()
},
onScroll() {
// 到底拉取更多數(shù)據(jù)
if(Tool.touchBottom()) {
this.getList()
}
}
}
}
</script>
總之,遇到一些有想對(duì)比較固定的部分,包括js操作或者結(jié)構(gòu)固定,又有一些動(dòng)態(tài)的部分,我們應(yīng)該就應(yīng)該考慮到使用:組件+slot。
意向不到的slot另類用法
我在做需求的時(shí)候,做了一個(gè)組件,該組件分為上下兩個(gè)部分,這兩個(gè)部分耦合度很高(不然我怎么把它當(dāng)成一個(gè)組件呢哈哈哈),如下圖所示:

本來(lái)C區(qū)域是一個(gè)組件,然后產(chǎn)品突然說(shuō),需要把這兩個(gè)部分分開(kāi),把A移到C1的位置,C1移到A的位置(心里感覺(jué)到憋屈)。
這里我的第一個(gè)想法就是拆開(kāi)來(lái)做成兩個(gè)組件,但是問(wèn)題來(lái)了,之前這兩部分的耦合度很高,如果強(qiáng)制把它拆開(kāi)成兩個(gè)組件,那么這兩個(gè)組件之間的交互必然會(huì)多很多。比如,C1改變了某個(gè)東西會(huì)影響到C2,那么C1需要觸發(fā)事件通知父組件,父組件再調(diào)用C2的某個(gè)方法來(lái)更新?tīng)顟B(tài)。這種跨組件之間的通訊在組件之間頻繁交互的情況下,將會(huì)是噩夢(mèng),而我這邊卻需要頻繁的交互,所以如果把它拆分為兩個(gè)組件,那么工作量和復(fù)雜度將會(huì)大大的增加。當(dāng)然,你可以想到通過(guò)Event Hub的方式來(lái)實(shí)現(xiàn)兩個(gè)組件之間的交互,但是根本問(wèn)題還是沒(méi)有實(shí)質(zhì)性得得到解決。
那么,有什么方法可以做到不拆分成兩個(gè)組件又能移動(dòng)位置的方法呢,答案就是slot。以我的例子為例,把A和B作為C的內(nèi)容分發(fā),原來(lái)是這樣的:
<A></A> <B></B> <C></C>
改為slot以后是這樣的
<C> <A slot="c1"></A> <B slot="c2"></B> </C>
這樣就能做到不把C模塊拆分,又能調(diào)整位置了,以最小的代價(jià)完成需求~~。
總結(jié)
vue的slot不僅可以用來(lái)內(nèi)容分發(fā),還可以用來(lái)做位置調(diào)整。如果在需要拆分組件來(lái)做位置調(diào)整,又不想因?yàn)椴鸱竹詈隙群芨叩慕M件,可以考慮使用slot來(lái)進(jìn)行位置調(diào)整。一點(diǎn)愚見(jiàn),希望對(duì)大家有所幫助。也希望大家多多支持腳本之家。
相關(guān)文章
基于vue實(shí)現(xiàn)一個(gè)禪道主頁(yè)拖拽效果
最近在做一個(gè)基于vue的后臺(tái)管理項(xiàng)目。接下來(lái)通過(guò)本文給大家分析一款基于vue做一個(gè)禪道主頁(yè)拖拽效果,需要的朋友可以參考下2019-05-05
vue3 provide和inject底層組件的值不是響應(yīng)式的處理詳解
這篇文章主要為大家介紹了vue3 provide和inject底層組件的值不是響應(yīng)式的處理詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-08-08
vue3中使用ant-design-vue的layout組件實(shí)現(xiàn)動(dòng)態(tài)導(dǎo)航欄和面包屑功能
這篇文章主要介紹了vue3中使用ant-design-vue的layout組件實(shí)現(xiàn)動(dòng)態(tài)導(dǎo)航欄和面包屑功能,基于一個(gè)新建的Vue3項(xiàng)目上實(shí)現(xiàn),本文結(jié)合示例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下2023-01-01
vue項(xiàng)目中使用particles實(shí)現(xiàn)粒子背景效果及遇到的坑(按鈕沒(méi)有點(diǎn)擊響應(yīng))
為了提高頁(yè)面展示效果,登錄界面內(nèi)容比較單一的,粒子效果作為背景經(jīng)常使用到,vue工程中利用vue-particles可以很簡(jiǎn)單的實(shí)現(xiàn)頁(yè)面的粒子背景效果,本文給大家分享在實(shí)現(xiàn)過(guò)程中遇到問(wèn)題,需要的朋友一起看看吧2020-02-02
Vue3中Provide?/?Inject的實(shí)現(xiàn)原理分享
provide和inject主要為高階插件/組件庫(kù)提供用例,這篇文章主要給大家介紹了關(guān)于Vue3中Provide?/?Inject的實(shí)現(xiàn)原理,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2022-02-02
Vue form表單動(dòng)態(tài)添加組件實(shí)戰(zhàn)案例
這篇文章主要介紹了Vue form表單動(dòng)態(tài)添加組件實(shí)戰(zhàn)案例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-09-09
vue.js iview打包上線后字體圖標(biāo)不顯示解決辦法
這篇文章主要介紹了vue.js iview打包上線后字體圖標(biāo)不顯示解決辦法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-01-01

