從組件封裝看Vue的作用域插槽的實(shí)現(xiàn)
作用域插槽不是那么直觀的一個(gè)概念。Vue文檔使用了一段描述性的話來解釋作用域插槽:
有的時(shí)候你希望提供的組件帶有一個(gè)可從子組件獲取數(shù)據(jù)的可復(fù)用的插槽
……
但是在我們應(yīng)用的某些部分,我們希望每個(gè)獨(dú)立的待辦項(xiàng)渲染出和 todo.text 不太一樣的東西。這也是作用域插槽的用武之地。
但在我看來,至少是第一次讀到的時(shí)候,這段話相當(dāng)不好理解。插槽不是分發(fā)內(nèi)容到子組件嗎,為什么還要從子組件中獲取數(shù)據(jù)?不是已經(jīng)有了通過emit事件的方法從子組件向父組件傳遞數(shù)據(jù)嗎,為什么需要它?作用域插槽到底是來干嘛的?……
在瀏覽了不少博客、自己思考“如果不這么做,就會(huì)怎么樣”再動(dòng)手實(shí)踐之后,作用域插槽的含義才逐漸明了。其實(shí)作用域插槽提供了一種封裝可復(fù)用組件的新思路。下面我會(huì)從最簡(jiǎn)單的例子開始。
簡(jiǎn)單的展示列表
現(xiàn)在我們做一個(gè)純展示用途的列表組件,如下圖所示:

第一個(gè)例子先用slot來分發(fā)內(nèi)容
<template>
<div class="list">
<div class="list-title">
<slot name="title"></slot>
</div>
<div class="list-content">
<slot name="content"></slot>
</div>
</div>
</template>
<script>
export default {
name: "MyList"
}
</script>
在父組件中使用MyList
<template>
<MyList>
<span slot="title">title</span>
<ul slot="content">
<li v-for="item in listData">{{item}}</li>
</ul>
</MyList>
</template>
<script>
import myList from './List.vue';
export default {
name: 'HelloWorld',
components: {
'MyList': myList
},
data() {
return {
listData: [
'列表項(xiàng)1',
'列表項(xiàng)2',
'列表項(xiàng)3'
]
}
}
}
</script>
省略了其中的樣式代碼,結(jié)果如圖所示

滿足了基本的需求,但是作為組件的使用者,這樣的一個(gè)組件會(huì)讓我覺得非常麻煩,content中循環(huán)的邏輯還需要我自己動(dòng)手來寫,這樣的使用毫無便利性。于是有了下面第二個(gè)版本
使用prop來傳遞數(shù)據(jù)
因?yàn)榭紤]到列表的內(nèi)容總是一個(gè)數(shù)組,我把循環(huán)結(jié)構(gòu)寫進(jìn)了組件中
列表組件第二版:
<template>
<div class="list">
<div class="list-title">{{title}}</div>
<ul class="list-content">
<li v-for="(item ,index) in content" :key="index">{{item}}</li>
</ul>
</div>
</template>
<script>
export default {
name: "MyList",
props: {
title: {
type: String,
required: true
},
content: {
type: Array,
required: true
}
}
}
</script>
使用起來也非常方便,只需通過prop將數(shù)據(jù)傳入組件中
<template>
<div>
<MyList title="標(biāo)題1" :content="listData"></MyList>
<MyList title="標(biāo)題2" :content="newListData"></MyList>
</div>
</template>
<script>
import myList from './List.vue';
export default {
name: 'HelloWorld',
components: {
'MyList': myList
},
data() {
return {
listData: [
'列表項(xiàng)1',
'列表項(xiàng)2',
'列表項(xiàng)3'
],
newListData: [
'新的列表項(xiàng)1',
'新的列表項(xiàng)2',
'新的列表項(xiàng)3'
],
}
}
}
</script>
改進(jìn)之后,每當(dāng)我使用組件只需一行代碼,大大簡(jiǎn)化了工作量

易用性的需求也滿足了,但現(xiàn)在又有了新的問題,組件的拓展性不好!每次只能生成相同結(jié)構(gòu)的列表,一旦業(yè)務(wù)需求發(fā)生了變化,組件就不再適用了。比如我現(xiàn)在有了新的需求,在一個(gè)列表的每個(gè)列表項(xiàng)前加入了一個(gè)小logo,我總不可能又寫一個(gè)新的組件來適應(yīng)需求的變化吧?假如需要更多的定制化場(chǎng)景呢?
作用域插槽
這里就有了第三版的列表組件,使用作用域插槽將子組件中的數(shù)據(jù)傳遞出去
<template>
<div class="list">
<div class="list-title">{{title}}</div>
<ul class="list-content">
<li v-for="(item ,index) in content" :key="index">
<!--這里將content中的每一項(xiàng)數(shù)據(jù)綁定到slot的item變量上,在父組件中可以獲取到item變量-->
<slot :item="item">{{item}}</slot>
</li>
</ul>
</div>
</template>
使用組件時(shí),將業(yè)務(wù)所需的content模板傳入
<template>
<div>
<MyList title="標(biāo)題1" :content="listData1"></MyList>
<MyList title="標(biāo)題2" :content="listData2">
<template slot-scope="scope">
<img :src="scope.item.img" width="20">
<span>{{scope.item.text}}</span>
</template>
</MyList>
<MyList title="標(biāo)題3" :content="listData3">
<template slot-scope="scope">
<b>{{scope.item.prefix ? '有前綴' : '無前綴'}}</b>
<span>{{scope.item.text}}</span>
<span>{{scope.item.remark}}</span>
</template>
</MyList>
</div>
</template>
<script>
import myList from './List.vue';
export default {
name: 'HelloWorld',
components: {
'MyList': myList
},
data() {
return {
listData1: [
'列表項(xiàng)1',
'列表項(xiàng)2',
'列表項(xiàng)3'
],
listData2: [
{text: '第二個(gè)列表的列表項(xiàng)1', img: 'example.png'},
{text: '第二個(gè)列表的列表項(xiàng)2', img: 'example.png'},
{text: '第二個(gè)列表的列表項(xiàng)3', img: 'example.png'}
],
listData3: [
{text: '第三個(gè)列表的列表項(xiàng)1', prefix: true, remark: '附加的備注1'},
{text: '第三個(gè)列表的列表項(xiàng)2', prefix: false, remark: '附加的備注2'},
{text: '第三個(gè)列表的列表項(xiàng)3', prefix: true, remark: '附加的備注3'}
],
}
}
}
</script>
實(shí)現(xiàn)了定制化的列表

再回到開始的問題,作用域插槽到底是干嘛用的?很顯然,它的作用就如官網(wǎng)所說的一樣:將組件的數(shù)據(jù)暴露出去。而這么做,給了組件的使用者根據(jù)數(shù)據(jù)定制模板的機(jī)會(huì),組件不再是寫死成一種特定的結(jié)構(gòu)。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Vue路由與a標(biāo)簽鏈接錨點(diǎn)發(fā)生沖突問題及解決
這篇文章主要介紹了Vue路由與a標(biāo)簽鏈接錨點(diǎn)發(fā)生沖突問題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-03-03
vuejs+element UI table表格中實(shí)現(xiàn)禁用部分復(fù)選框的方法
今天小編就為大家分享一篇vuejs+element UI table表格中實(shí)現(xiàn)禁用部分復(fù)選框的方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2019-09-09
微信小程序Echarts動(dòng)態(tài)使用及圖表層級(jí)踩坑解決方案
這篇文章主要為大家介紹了微信小程序Echarts動(dòng)態(tài)使用及圖表層級(jí)踩坑解決方案,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-03-03
Vue 頁面狀態(tài)保持頁面間數(shù)據(jù)傳輸?shù)囊环N方法(推薦)
vue router給我們提供了兩種頁面間傳遞參數(shù)的方式,一種是動(dòng)態(tài)路由匹配,一種是編程式導(dǎo)航,接下來通過本文給大家介紹Vue 頁面狀態(tài)保持頁面間數(shù)據(jù)傳輸?shù)囊环N方法,需要的朋友可以參考下2018-11-11
基于Vue3實(shí)現(xiàn)印章徽章組件的示例代碼
這篇文章主要介紹了如何利用vue3實(shí)現(xiàn)簡(jiǎn)單的印章徽章控件,文中通過示例代碼講解詳細(xì),需要的朋友們下面就跟隨小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-04-04

