先假設咱們須要實現以下效果,並將其封裝成一個組件,固然目的並非要真的封裝組件,而是在這個過程當中學習 插槽 的使用以及優化的思路哈: 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> 複製代碼
這樣咱們的組件就完成了,不只靈活性更高,代碼健壯性也更好。 總得來講,插槽仍是很好用的,特別是在封裝組件的時候。