春節Vue-2.6.0發佈後,對slot插槽進行了一定的優化,那麼對於插槽,你真正的瞭解嗎?以下是我使用中總結的拙見,歡迎批評指正+++++
最近在做業務組件的時候,用到三種slot,被叫做”默認插槽“,”具名插槽“,”作用域插槽“。
默認插槽
子組件
<a v-bind:href="url" class="nav-link">
<slot>content</slot>
</a>
父組件
<navigation-link url="/profile">
Your Profile
</navigation-link>
如果父組件爲這個插槽提供了內容Your Profile,則默認的內容content會被替換掉。
具名插槽
如果子組件中的多個位置都需要父組件塞進不同的內容時,你需要使用具名插槽。
<div class="container">
<header>
<slot name="header"></slot>
</header>
<main>
<slot name-"main"></slot>
</main>
<footer>
<slot name="footer"></slot>
</footer>
</div>
子組件在header main footer三處需要塞入不同的內容,在父組件的 <template> 元素上使用 slot特性:
<base-layout>
<template slot="header">
<h1>Here might be a page title</h1>
</template>
<p>A paragraph for the main content.</p>
<p>And another one.</p>
<template slot="footer">
<p>Here's some contact info</p>
</template>
</base-layout>
看起來具名插槽能解決大部分的問題了,但是,在這種情況面前使用具名插槽會報錯:
<div v-for="(item, index) in resultList" :key="index">
<div class="list-content">
<p class="content" v-html="item.content"/>
<slot name="content"></slot>
</div>
</div>
當使用v-for迭代,同一具名插槽重複出現的時候,瀏覽器就會報錯
這時我們不得不使用作用域插槽
作用域插槽
在 <template> 上使用特殊的 slot-scope 特性,可以接收傳遞給插槽的 prop
子組件內兩處地方放置兩處插槽
<!--位置1-->
<slot name="tips" :tips="item.access"></slot>
<!--位置2-->
<slot name="content" :content="item.content"></slot>
父組件-使用時,slot=name 具名,調用scope.[name]
<template slot="tips" slot-scope="scope">
<span class="item-header-state" v-if="scope.tips === true"><i class="el-icon-check" />已認證</span>
</template>
<template slot="content" slot-scope="scope">
<el-tag
v-for="(tag, index) in scope.content.labels"
:key="index"
closable
type="info"
size="small"
:disable-transitions="false"
@close="handleCloseTag(tag, scope.content)">
{{tag.label}}
</el-tag>
</template>
But 自 2.6.0 起以上被廢棄。新推薦的語法請查閱這裏。
【以下爲引用內容】
插槽和具名插槽
父組件以另外一種方式(不是通過常規的 Props 屬性傳遞機制)向子組件傳遞信息。我發現把這種方法同常規的 HTML 元素聯繫起來很有幫助。
比如說 HTML 標籤。
<a href=”/sometarget">This is a link</a>
如果這是在 Vue 環境中並且 <a>
是你的組件,那麼你需要發送“This is a link”信息到‘a’組件裏面,然後它將被渲染成爲一個超鏈接,而“This is a link”就是這個鏈接的文本。
讓我們定義一個子組件來展示它的機制是怎樣的:
<template>
<div>
<slot></slot>
</div>
</template>
然後在父組件我們這麼做:
<template>
<div>
<child-component>This is from outside</child-component>
</div>
</template>
這時候屏幕上呈現的就應該和你預期的一樣就是“This is from outside”,但這是由子組件所渲染出來的。
我們還可以給子組件添加默認的信息,以免到時候這裏出現什麼都沒有傳入的情況,就像這樣子:
<template>
<div>
<slot>Some default message</slot>
</div>
</template>
然後如果我們像這樣子創建我們的子組件:
<child-component>
</child-component>
我們可以看到屏幕上會呈現“Some default message”。
具名插槽和常規插槽非常類似,唯一的差別就是你可以在你的目標組件多個位置傳入你的文本。
我們把子組件升級一下,讓它有多個具名插槽
<template>
<div>
<slot>Some default message</slot>
<br/>
<slot _name_="top"></slot>
<br/>
<slot _name_="bottom"></slot>
</div>
</template>
這樣,在我們的子組件中就有了三個插槽。其中 top 和 bottom 插槽是具名插槽。
讓我們更新父組件以使用它。
<child-component _v-slot:top_>
Hello there!
</child-component>
注意 —— 我們在這裏使用新的 Vue 2.6 語法來指定我們想要定位的插槽:v-slot:theName
。
你現在認爲會在屏幕上看到什麼呢?如果你說是“Hello Top!”,那麼你就只說對了一部分。
因爲我沒有爲沒有具名的插槽賦予任何值,我們因此也還會得到默認值。所以我們真正會看到的是:
Some default message
Hello There!
其實真正意義上沒有具名的插槽是被當作‘default’,所以你還可以這麼做:
<child-component _v-slot:default_>
Hello There!
</child-component>
現在我們就只會看到:
Hello There!
因爲我們已經提供了值給默認(也就是未具名)插槽,因此具名插槽‘top’和‘bottom’也都沒有默認值。
你發送的並不一定只是文本,還可以是其他組件或者 HTML。你可以發送任意你想展示的內容。
作用域插槽
我認爲插槽和具名插槽相對簡單,一旦你稍微玩玩就可以掌握。可另一方面,作用域插槽雖然名字相似但又有些不同之處。
我傾向於認爲作用域插槽有點像一個放映機(或者是一個我歐洲朋友的投影儀)。以下是原因。
子組件中的作用域插槽可以爲父組件中的插槽的顯示提供數據。這就像一個人帶着放映機站在你的子組件裏面,然後在父組件的牆上讓一些圖像發光。
這有一個例子。在子組件中我們像這樣設置了一個插槽:
<template>
<div>
<slot _name_="top" _:myUser_="user"></slot>
<br/>
<slot _name_="bottom"></slot>
<br/>
</div>
</template>
<script>
data() {
_return_ {
user: "Ross"
}
}
</script>
注意到我們的具名插槽‘top’現在有了一個名爲‘myUser’的屬性,然後我們綁定了一個動態的值在‘user’中。
在我們的父組件中就像這樣子設置子組件:
<div>
<child-component _v-slot:top_="slotProps">{{ slotProps }}</child-component>
</div>
我們在屏幕上看到的就是這樣子:
{ “myUser”: “Ross” }
還是使用放映機的類比,我們的子組件通過 myUser 對象將其用戶字符串的值傳遞給父組件。它在父組件上投射到的牆就被稱爲‘slotProps’。
我知道這不是一個完美的類比,但當我第一次嘗試理解這個機制的時候,它幫助我以這種方式思考。
Vue 的文檔非常好,而且我也已經看到了一些其他關於作用域插槽工作機制的說明。但很多人採取的方法似乎是將父組件中的所有或部分屬性命名爲與子組件相同,我認爲這會使得數據很難被追蹤。
在父組件中使用 ES6 解構,我們這樣子寫還可以將特定 user 對象從插槽屬性(你可以隨便怎麼稱呼它)解脫出來:
<child-component _v-slot:top_="{myUser}">{{ myUser }}</child-component>
或者甚至就只是在父組件中給它一個新的名字:
<child-component _v-slot:top_="{myUser: aFancyName}">{{ aFancyName }}</child-component>
所有都是通過 ES6 解構,與 Vue 本身並沒有什麼關係。
如果你正開始使用 Vue 和插槽,希望這可以讓你起步並解決一些棘手的問題。