在網上搜了不少關於做用域插槽的解釋,感受沒有寫得很具體的吧,我認爲應該對組件化有很深的理解纔會觸及到這個問題吧,這裏也分享下我本身對於slot-scope的一點理解。vue
// 子組件
<template>
<slot>來啊,我這裏挖了個坑</slot>
</template>
// 父組件
<template>
<child>
<!-- 傳入子組件的自定義內容,會填入到子組件的slot插槽中 -->
<span>我在這放個span,樂意的話,放個組件都行</span>
</child>
</template>
複製代碼
// 子組件
<template>
<section>
<slot name="article-title">這裏放標題</slot>
<slot>這裏放做者</slot>
<slot name="article-content">這裏放文章內容</slot>
</section>
</template>
// 父組件
<template>
<section>
<slot-child>
<h1 slot="article-title">vue做用域插槽,你真的懂了嗎?</h1>
<p slot="article-content">好像有點懂了</p>
<div>王五</div>
</slot-child>
</section>
</template>
複製代碼
<slot :nickName="'Tusi'"></slot>
複製代碼
而父組件經過slot-scope綁定的對象下拿到nickName的值。bash
<template>
<section>
<slot-child>
<template slot-scope="scope">
<div>{{scope.nickName}}</div>
</template>
</slot-child>
</section>
</template>
複製代碼
這裏你們應該都有疑問。這有什麼用?我在子組件用$emit向父組件傳遞數據不就好了?組件化
我以爲要從組件之間的數據流向來思考做用域插槽的應用場景。佈局
假設第一個場景,須要你寫一個商品卡片組件,並經過循環去展現多個卡片,而且要求能響應每一個卡片上的圖片或者其餘內容的點擊事件而跳轉到商品詳情頁,你會怎麼寫?spa
我會使用以下的處理方式,首先將商品卡片寫成一個組件Commodity.vue,而在CommodityList.vue中用一個v-for來處理商品卡片列表的展現。code
<commodity v-for="(item,index) in commodities" @clickCommodity="onCommodityClick"></commodity>
複製代碼
Commodity組件經過$emit像父組件傳遞clickCommodity事件,並攜帶商品數據,父組件便可在onCommodityClick方法中獲得數據,進行業務處理,這樣便完成了一個基本的由子到父的數據傳遞。cdn
若是再往上抽象一下呢?好比我有多個運營欄目,像淘寶首頁有「有好貨」,「愛逛街」這樣兩個欄目,每一個欄目下都須要有一個商品卡片列表,那麼商品卡片列表CommodityList.vue就要抽成組件了。而這個包含多個運營欄目的vue組件我假設它叫ColumnList.vue,在其中經過v-for調用了CommodityList組件。對象
注意:業務來了,我但願把點擊商品卡片的業務放在ColumnList.vue中處理。大家想象一下要怎麼作?一種土辦法就是商品按鈕點擊時,Commodity組件$emit通知CommodityList.vue,而CommodityList接着把事件用$emit往上拋,那麼ColumnList.vue就能處理這個點擊事件了。這樣作徹底沒有問題,可是顯得子組件很不純粹,跟業務都扯上關係了。blog
那麼如何優雅地解決這個問題呢?這個時候,做用域插槽真正派上用場了。事件
經過做用域插槽將本應該由CommodityList處理的商品卡片點擊業務onCommodityClick提高到ColumnList處理。
<el-row :gutter="20">
<el-col :span="12" v-for="(column, index) in columnList" :key="index">
<el-card class="box-card card-column">
<div slot="header" class="clearfix">
<span>{{column.columnName}}</span>
</div>
<commodity-list :commodities="column.commodityList">
<template slot-scope="scope">
<!-- 這裏只須要給Commodity組件傳入數據,響應Commodity組件的clickCommodity事件便可。
事件沒必要攜帶參數,徹底符合父到子的數據流向,而不會發生子組件又給父組件反向發數據的狀況 -->
<commodity :modityData="scope.row" @clickCommodity="onCommodityClick(scope.row)"></commodity>
</template>
</commodity-list>
</el-card>
</el-col>
</el-row>
複製代碼
而CommodityList組件內部應該是改形成這樣,slot接收來自父組件的商品卡片組件,這裏面不涉及關於商品組件的業務,只關注其餘業務和佈局便可。最終就實現了組件和業務的剝離,這也是組件化的精髓所在吧。不知道有沒有幫到您呢?
<el-row :gutter="20">
<el-col :span="8" v-for="(item, index) in commodities" :key="index" style="margin-top:20px;">
<slot :row="item"></slot>
</el-col>
</el-row>
複製代碼
這是我實現的效果,忽略樣式吧,原理都懂了,作個漂亮的卡片有多難?
總結一下,做用域插槽適合的場景是至少包含三級以上的組件層級,是一種優秀的組件化方案!