從一個簡單的 list 組件搞懂 Vue 插槽

需求

先假設咱們須要實現以下效果,並將其封裝成一個組件,固然目的並非要真的封裝組件,而是在這個過程當中學習 插槽 的使用以及優化的思路哈: html

一看到這個效果,有的小夥伴可能就要說了:這還不簡單,反手就是一段代碼甩上來

<template>
  <div>
    <div>{{ title }}</div>
    <hr>
    <div>
      <div v-for="(item, i) in List" :key="i">{{ item }}</div>
    </div>
  </div>
</template>
複製代碼

而後在父組件中這樣調用就 ok 了:編程

<template>
  <custom-list :title="title" :List="books" />
</template>

<script> import CustomList from './custom-list.js'; export default { components: { CustomList }, data() { return { title: '書籍列表', books: ['Dom編程藝術', '你不知道的Javascript', 'CSS世界', 'HTML入門教程'], }; }, }; </script>
複製代碼

Emmm。。乍一看彷佛沒有任何毛病,甚至數據變化了也是能夠知足要求的。markdown

然而,這樣就夠了嗎?需求老是善變的,好比說,有一天又要知足以下效果呢?學習

此時,這個組件就有點不夠用了,沒法應對各類需求的組件不是好組件。既然內容是多變的,那就不能固定,而應該是靈活的,最靈活的方式應該是將決定權交給使用者(父組件),使用者是最清楚內容應該是什麼的,那具體怎麼作呢?

插槽

這個時候就到咱們的 插槽 登場了。插槽容許咱們將子組件中的內容分發到父組件,由父組件決定子組件要渲染的內容。優化

普通插槽

仍是以上述組件爲例,咱們須要將子組件中 title的內容分發出去,由父組件將內容傳進來。此時咱們能夠這樣修改子組件:spa

<template>
  <div>
    <div>
      <slot></slot>
    </div>
    <hr>
    <div>
      <div v-for="(item, i) in List" :key="i">{{ item }}</div>
    </div>
  </div>
</template>
複製代碼

而後在父組件中調用:code

<template>
  <custom-list :List="books">
    <span><icon type="waves"> 書籍列表</span>
  </custom-list>
</template>

<script> import CustomList from './custom-list.js'; export default { components: { CustomList }, data() { return { title: '書籍列表', books: ['Dom編程藝術', '你不知道的Javascript', 'CSS世界', 'HTML入門教程'], }; }, }; </script>
複製代碼

這樣的話,父組件中包裹的內容就會替代 <slot> 標籤,因此最終的渲染結果就是這樣的:component

<template>
  <div>
    <div>
      <span><icon type="waves"> 書籍列表</span>
    </div>
    <hr>
    <div>
      <div v-for="(item, i) in List" :key="i">{{ item }}</div>
    </div>
  </div>
</template>
複製代碼

順便提一嘴:在 <slot> 標籤中也是能夠放置內容的,如:<slot> 標題 </slot>, 這表示插槽的默認內容,只有當父組件沒有經過插槽傳遞內容的時候,纔會顯示該默認內容。orm

同理咱們的 content 的內容也是須要分發出去的,因而咱們再修改:htm

<template>
  <div>
    <div>
      <slot></slot>
    </div>
    <hr>
    <slot></slot>
  </div>
</template>
複製代碼

這樣一來又有問題了:咱們定義了兩個 怎麼去區分它們的內容呢,總不能靠書寫順序吧?這顯然是不靠譜的。因而,就有了 具名插槽

具名插槽

顧名思義就是帶名字的插槽,便於多個插槽之間的區分。而後咱們繼續修改:

<template>
  <div>
    <div>
      <slot name="title"></slot>
    </div>
    <hr>
    <slot name="content"></slot>
  </div>
</template>
複製代碼

給每一個插槽定義一個 name 屬性,而後咱們在父組件傳值的時候帶上對應插槽的 name 就好了。

<template>
  <custom-list>
    <template v-slot:title>
      <span><icon type="waves"> 書籍列表</span>
    <template>
    <template v-slot:content>
      <div>
        <div v-for="(item, i) in book" :key="i">
          <icon :type="item.icon">{{ item.name }}
        </div>
      </div>
    </template>
  </custom-list>
</template>

<script> import CustomList from './custom-list.js'; export default { components: { CustomList }, data() { return { title: '書籍列表', books: [ { name: 'Dom編程藝術',icon: 'face' }, { name: '你不知道的Javascript', icon: 'favorite' }, { name: 'CSS世界', icon: 'av_timer'}, { name: 'HTML入門教程', icon: 'star_half' }, ], }; }, }; </script>
複製代碼

在向具名插槽提供內容的時候,咱們能夠在一個 <template> 元素上使用 v-slot 指令,並以 v-slot 的參數的形式提供其名稱。

這下可好了,連數據都不用傳了,List 在子組件根本用不到,數據直接在父組件中就渲染了,並且每次使用這個組件的時候都要寫一遍 v-for,那爲何不把 v-for 寫在子組件中呢?這樣就只須要寫一次了。 那咱們再來修改子組件:

<template>
  <div>
    <div>
      <slot name="title"></slot>
    </div>
    <hr>
    <div>
      <div v-for="(item, i) in List">
        <slot name="item"></slot>
      </div>
    </div>
  </div>
</template>
複製代碼

這樣又有問題了:我要顯示 item 可是 item 是子組件中的數據,當父組件經過 v-slot 傳遞內容進來的時候,<slot>{{ item }}</slot> 的內容就會被傳進來的內容替換了。這咋搞呢?思考一下,若是咱們將數據給父組件,在父組件中顯示不就解決了麼?那麼這個時候就要輪到 做用域插槽 登場了。

做用域插槽

做用域插槽容許咱們將子組件的數據傳給父組件,這不正好解決了上面的問題麼?下面咱們再調整子組件

<template>
  <div>
    <div>
      <slot name="title"></slot>
    </div>
    <hr>
    <div>
      <div v-for="(item, i) in List">
        <slot name="item" :row="item"></slot>
      </div>
    </div>
  </div>
</template>
複製代碼

此時咱們將每一個 item 的值經過 row 屬性傳給了 <slot>, 而後咱們在父組件中取出這個值:

<template>
  <custom-list>
    <template v-slot:title>
      <span><icon type="waves"> 書籍列表</span>
    <template>
    <!--實際傳的數據格式爲: { row:{ name: xx, icon: xx } },因此這裏可使用結構賦值-->
    <template v-slot:item="{ row }">
      <icon :type="row.icon">{{ row.name }}
    </template>
  </custom-list>
</template>
複製代碼

這樣咱們的組件就完成了,不只靈活性更高,代碼健壯性也更好。 總得來講,插槽仍是很好用的,特別是在封裝組件的時候。

相關文章
相關標籤/搜索