vue做用域插槽,你真的懂了嗎?

前言

在網上搜了不少關於做用域插槽的解釋,感受沒有寫得很具體的吧,我認爲應該對組件化有很深的理解纔會觸及到這個問題吧,這裏也分享下我本身對於slot-scope的一點理解。vue

  • slot你們看看文檔都懂了,無非就是在子組件中挖個坑,坑裏面放什麼東西由父組件決定。
// 子組件
<template>
  <slot>來啊,我這裏挖了個坑</slot>
</template>

// 父組件
<template>
  <child>
   <!-- 傳入子組件的自定義內容,會填入到子組件的slot插槽中 -->
    <span>我在這放個span,樂意的話,放個組件都行</span>
  </child>
</template>
複製代碼
  1. 給slot傳入普通文本

slot傳入普通文本

  1. 給slot傳入了一個圖像處理組件
    slot傳入普通文本
  • 具名插槽也很簡單,好比有多個插槽,我做爲父組件,確定想區別子組件中的幾個插槽,那就要用slot標籤的name屬性來標識了,而父組件要決定在什麼插槽裏面放什麼內容,就要將name的值賦值給slot屬性傳遞給對應的插槽。若是slot沒有name屬性,就是匿名插槽了,而父組件中不指定slot屬性的內容,就會被丟到匿名插槽中。
// 子組件
<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標籤上綁定屬性值,如:
<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>
複製代碼

這是我實現的效果,忽略樣式吧,原理都懂了,作個漂亮的卡片有多難?

淘寶運營欄目列表
淘寶運營欄目列表

總結一下,做用域插槽適合的場景是至少包含三級以上的組件層級,是一種優秀的組件化方案!

相關文章
相關標籤/搜索