vue的內建元件slot
vue有很多內建的元件,有component,transition, transition-group, keep-alive, slot;其中slot(插槽)官方文件的解釋為內容分發的出口,官方解釋很抽象,我們不如直接來根據名字插槽來理解,即留出一個位置,你可以插入任何你想顯示的東西例如文字或者hml標籤
為什麼要用slot?
vue最大的特性就是元件化,在實際專案開發中都會封裝公用元件,增強程式碼複用性。而公用元件就是要可以在多處引用,這就要求網站多處的渲染解構或效果要完全一樣,才能把一樣的這部分拿出來封裝成一個公用元件多處引用,但實際專案開發中設計出來的網站很少有多處效果完全一樣的情況,往往都是大體效果類似,並不完全一樣,多少會有一些差別。這時候就會用到元件的slot了,封裝公用元件的時候把不一樣的地方作為一個插槽留出來,在引用元件的時候插入什麼就顯示什麼。這樣就實現了元件的高度複用。
使用場景
假如我現在要畫兩幅畫,第一幅是一個籃子裡裝著蘋果,第二幅是一個籃子裡裝著橘子,如果兩幅畫的籃子裡裝著一樣的,我只需要畫一幅,然後複製貼上就好了。slot的解決辦法:畫一個籃子把蘋果或者橘子的空位留出來,複製貼上出來兩個籃子,當我要用第一幅畫的時候,給籃子裡畫上蘋果,用第二幅畫的時候給籃子裡畫上橘子。
例如網站裡,有很多像如下這樣的tab標籤
這樣定義公用元件myTab.vue:
<div>
<div class="mytab">
<span v-for="item in showItems" :key="item.name">{{item.label}}</span>
<slot></slot> // 插槽,給蘋果或橘子留出來的位置
</div>
</div>
複製程式碼
第一個地方呼叫:
import myTab from '@/components/myTab/myTab.vue' //匯入元件
... //此處省略其他語句
components: {
myTab //註冊元件
},
...
//呼叫
<my-tab :showItems="showItems">
<i class="iconfont"></i> // 插入篩選圖示(畫蘋果)
</my-tab>
最終渲染結果:
<div>
<div class="mytab">
<span v-for="item in showItems" :key="item.name">{{item.label}}</span>
<i class="iconfont"></i>
</div>
</div>
複製程式碼
第二個地方呼叫:
import myTab from '@/components/myTab/myTab.vue' //匯入元件
...
components: {
myTab //註冊元件
},
...
//呼叫
<my-tab :showItems="showItems">
<span class="slotHandle"> // 插入退出檢視按鈕(畫橘子)
退出檢視
<i class="iconfont"></i>
</span>
</my-tab>
最終渲染結果:
<div>
<div class="mytab">
<span v-for="item in showItems" :key="item.name">{{item.label}}</span>
<span class="slotHandle">
退出檢視
<i class="iconfont"></i>
</span>
</div>
</div>
複製程式碼
當然了,用v-if通過prop傳參也完全可以實現這個效果: 定義公用元件myTab.vue:
<div>
<div class="mytab">
<span v-for="item in showItems" :key="item.name">{{item.label}}</span>
<i class="iconfont" v-if="type === '蘋果'"></i> // 插入篩選圖示(畫蘋果)
<span class="slotHandle" v-if="type === '橘子'"> // 插入退出檢視按鈕(畫橘子)
退出檢視
<i class="iconfont"></i>
</span>
</div>
</div>
...
export default{
prop: ['type']
}
複製程式碼
但是如果頁面上有一百類似的地方要呼叫這個元件,定義元件的時候就要寫一百個v-if來判斷到底該顯示什麼,這樣元件就顯得臃腫不直觀
slot的name屬性
有時候在一個元件裡我們需要用到多個插槽,這時候就要用到slot的name屬性
<div class="container">
<header>
... //其他佈局
<slot name="header"></slot>
... //其他佈局
</header>
<main>
... //其他佈局
<slot name='main'></slot>
... //其他佈局
</main>
<footer>
... //其他佈局
<slot name="footer"></slot>
... //其他佈局
</footer>
</div>
複製程式碼
呼叫時使用template標籤以及v-slot來匹配slot的name(v-slot是vue2.6及以上版本支援的語法,vue2.6以下版本請檢視文件):
<container>
<template v-slot:header>
<h1>Here might be a page title</h1>
</template>
// 不是必須寫的,沒有要插入的內容就不寫
<template v-slot:footer>
<p>Here's some contact info</p>
</template>
</container>
複製程式碼
slot訪問子元件的變數
slot是子元件留出來的插槽,插入的內容是由父元件定義的,也相當於插入的是父元件的標籤,這些要被插入的標籤當然可以隨意的訪問父元件的data屬性了
父元件:father.vue
<my-tab :showItems="showItems">
// 插入的內容開始
<span class="slotHandle">
{{btnText}} // 可以渲染出 退出檢視 按鈕
<i class="iconfont"></i>
</span>
//插入的內容結束
</my-tab>
...
import myTab from '@/components/myTab/myTab.vue' //匯入元件
...
components: {
myTab //註冊元件
},
data () {
return {
btnText: '退出檢視'
}
}
...
複製程式碼
如果要插入的標籤裡需要訪問子元件的變數(以下是vue2.6及以上版本支援的語法,vue2.6以下版本請檢視文件)
定義包含slot的子元件myTab.vue:
<div>
<div class="mytab">
<span v-for="item in showItems" :key="item.name">{{item.label}}</span>
<slot :sonCount='count'></slot> // 在子元件中可以隨意在slot上新增屬性(sonCount)
</div>
</div>
...
data () {
return {
count: 1
}
}
複製程式碼
父元件呼叫father.vue:
<my-tab :showItems="showItems" v-slot="myTabSlot"> ,// 注意時=,相當於: v-slot:default="myTabSlot"
// 插入的內容開始
{{myTabSlot.sonCount}} // 訪問到了tab.vue裡的count:1
//插入的內容結束
</my-tab>
...
import myTab from '@/components/myTab/myTab.vue' //匯入元件
...
components: {
myTab //註冊元件
}
...
複製程式碼
還記得v-slot是幹嘛的嗎?子元件有多個slot時用來匹配slot的name的,也就是v-slot的值是slot的name屬性,這樣就好理解了,我呼叫子元件的時候給子元件裡的slot起了個名字(myTabSlot),我給子元件中的slot上新增了個屬性(sonCount),繫結了count,用myTabSlot.sonCount就可以訪問到count變數了,感覺和ref很類似~
這裡有要注意的點: 官方說法: 當只有預設插槽時,元件的標籤才可以被當作插槽的模板來使用,這樣我們就可以把 v-slot 直接用在元件上。 翻譯解說: 當子元件定義slot時只有一個slot,且沒有給這一個slot新增name屬性,這個slot插槽就是預設插槽(其實預設插槽有一個預設的name為default),呼叫子元件時,子元件標籤(my-tab)可以代替template標籤,v-slot可以用在(my-tab)上,否則v-slot只能用在template標籤上
語法糖:
跟 v-on 和 v-bind 一樣,v-slot 也有縮寫,即把引數之前的所有內容 (v-slot:) 替換為字元 #,注意是v-slot:(有冒號)
例如上邊程式碼:v-slot:default="myTabSlot" 可以寫成 #default="myTabSlot",#後跟的是slot的name,如果是預設插槽,name為default,不能省略
----------------------------------------------------------完結--------------------------------------------------------