該頁面假設(shè)你已經(jīng)閱讀過了組件基礎(chǔ)。如果你還對組件不太了解,推薦你先閱讀它。
Vue 實現(xiàn)了一套內(nèi)容分發(fā)的 API,這套 API 的設(shè)計靈感源自 Web Components 規(guī)范草案,將 <slot>
元素作為承載分發(fā)內(nèi)容的出口。
它允許你像這樣合成組件:
<todo-button>
Add todo
</todo-button>
然后在 <todo-button>
的模板中,你可能有:
<!-- todo-button 組件模板 -->
<button class="btn-primary">
<slot></slot>
</button>
當(dāng)組件渲染的時候,將會被替換為“Add Todo”。
<!-- 渲染 HTML -->
<button class="btn-primary">
Add todo
</button>
不過,字符串只是開始!插槽還可以包含任何模板代碼,包括 HTML:
<todo-button>
<!-- 添加一個Font Awesome 圖標(biāo) -->
<i class="fas fa-plus"></i>
Add todo
</todo-button>
或其他組件
<todo-button>
<!-- 添加一個圖標(biāo)的組件 -->
<font-awesome-icon name="plus"></font-awesome-icon>
Add todo
</todo-button>
如果 <todo-button>
的 template 中沒有包含一個 <slot>
元素,則該組件起始標(biāo)簽和結(jié)束標(biāo)簽之間的任何內(nèi)容都會被拋棄
<!-- todo-button 組件模板 -->
<button class="btn-primary">
Create a new item
</button>
<todo-button>
<!-- 以下文本不會渲染 -->
Add todo
</todo-button>
當(dāng)你想在一個插槽中使用數(shù)據(jù)時,例如:
<todo-button>
Delete a {{ item.name }}
</todo-button>
該插槽可以訪問與模板其余部分相同的實例 property (即相同的“作用域”)。
插槽不能訪問 <todo-button>
的作用域。例如,嘗試訪問 action
將不起作用:
<todo-button action="delete">
Clicking here will {{ action }} an item
<!-- `action` 未被定義,因為它的內(nèi)容是傳遞*到* <todo-button>,而不是*在* <todo-button>里定義的。 -->
</todo-button>
作為一條規(guī)則,請記住:
父級模板里的所有內(nèi)容都是在父級作用域中編譯的;子模板里的所有內(nèi)容都是在子作用域中編譯的。
有時為一個插槽設(shè)置具體的后備 (也就是默認(rèn)的) 內(nèi)容是很有用的,它只會在沒有提供內(nèi)容的時候被渲染。例如在一個 <submit-button>
組件中:
<button type="submit">
<slot></slot>
</button>
我們可能希望這個 <button>
內(nèi)絕大多數(shù)情況下都渲染文本“Submit”。為了將“Submit”作為后備內(nèi)容,我們可以將它放在 <slot>
標(biāo)簽內(nèi):
<button type="submit">
<slot>Submit</slot>
</button>
現(xiàn)在當(dāng)我在一個父級組件中使用 <submit-button
> 并且不提供任何插槽內(nèi)容時:
<submit-button></submit-button>
后備內(nèi)容“Submit”將會被渲染:
<button type="submit">
Submit
</button>
但是如果我們提供內(nèi)容:
<submit-button>
Save
</submit-button>
則這個提供的內(nèi)容將會被渲染從而取代后備內(nèi)容:
<button type="submit">
Save
</button>
有時我們需要多個插槽。例如對于一個帶有如下模板的 <base-layout>
組件:
<div class="container">
<header>
<!-- 我們希望把頁頭放這里 -->
</header>
<main>
<!-- 我們希望把主要內(nèi)容放這里 -->
</main>
<footer>
<!-- 我們希望把頁腳放這里 -->
</footer>
</div>
對于這樣的情況,<slot>
元素有一個特殊的 attribute:name
。這個 attribute 可以用來定義額外的插槽:
<div class="container">
<header>
<slot name="header"></slot>
</header>
<main>
<slot></slot>
</main>
<footer>
<slot name="footer"></slot>
</footer>
</div>
一個不帶 name
的 <slot>
出口會帶有隱含的名字“default”。
在向具名插槽提供內(nèi)容的時候,我們可以在一個 <template>
元素上使用 v-slot
指令,并以 v-slot
的參數(shù)的形式提供其名稱:
<base-layout>
<template v-slot:header>
<h1>Here might be a page title</h1>
</template>
<template v-slot:default>
<p>A paragraph for the main content.</p>
<p>And another one.</p>
</template>
<template v-slot:footer>
<p>Here's some contact info</p>
</template>
</base-layout>
現(xiàn)在 <template>
元素中的所有內(nèi)容都將會被傳入相應(yīng)的插槽。
渲染的 HTML 將會是:
<div class="container">
<header>
<h1>Here might be a page title</h1>
</header>
<main>
<p>A paragraph for the main content.</p>
<p>And another one.</p>
</main>
<footer>
<p>Here's some contact info</p>
</footer>
</div>
注意,v-slot
只能添加在 <template>
上 (只有一種例外情況)
有時讓插槽內(nèi)容能夠訪問子組件中才有的數(shù)據(jù)是很有用的。當(dāng)一個組件被用來渲染一個項目數(shù)組時,這是一個常見的情況,我們希望能夠自定義每個項目的渲染方式。
例如,我們有一個組件,包含 todo-items 的列表。
app.component('todo-list', {
data() {
return {
items: ['Feed a cat', 'Buy milk']
}
},
template: `
<ul>
<li v-for="(item, index) in items">
{{ item }}
</li>
</ul>
`
})
我們可能需要替換插槽以在父組件上自定義它:
<todo-list>
<i class="fas fa-check"></i>
<span class="green">{{ item }}</span>
</todo-list>
但是,這是行不通的,因為只有 <todo-list>
組件可以訪問 item
,我們將從其父組件提供槽內(nèi)容。
要使 item
可用于父級提供的 slot 內(nèi)容,我們可以添加一個 <slot>
元素并將其綁定為屬性:
<ul>
<li v-for="( item, index ) in items">
<slot :item="item"></slot>
</li>
</ul>
綁定在 <slot
> 元素上的 attribute 被稱為插槽 prop?,F(xiàn)在在父級作用域中,我們可以使用帶值的 v-slot
來定義我們提供的插槽 prop 的名字:
<todo-list>
<template v-slot:default="slotProps">
<i class="fas fa-check"></i>
<span class="green">{{ slotProps.item }}</span>
</template>
</todo-list>
在這個例子中,我們選擇將包含所有插槽 prop 的對象命名為 slotProps
,但你也可以使用任意你喜歡的名字。
在上述情況下,當(dāng)被提供的內(nèi)容只有默認(rèn)插槽時,組件的標(biāo)簽才可以被當(dāng)作插槽的模板來使用。這樣我們就可以把 v-slot
直接用在組件上:
<todo-list v-slot:default="slotProps">
<i class="fas fa-check"></i>
<span class="green">{{ slotProps.item }}</span>
</todo-list>
這種寫法還可以更簡單。就像假定未指明的內(nèi)容對應(yīng)默認(rèn)插槽一樣,不帶參數(shù)的 v-slot
被假定對應(yīng)默認(rèn)插槽:
<todo-list v-slot="slotProps">
<i class="fas fa-check"></i>
<span class="green">{{ slotProps.item }}</span>
</todo-list>
注意默認(rèn)插槽的縮寫語法不能和具名插槽混用,因為它會導(dǎo)致作用域不明確:
<!-- 無效,會導(dǎo)致警告 -->
<todo-list v-slot="slotProps">
<todo-list v-slot:default="slotProps">
<i class="fas fa-check"></i>
<span class="green">{{ slotProps.item }}</span>
</todo-list>
<template v-slot:other="otherSlotProps">
slotProps is NOT available here
</template>
</todo-list>
只要出現(xiàn)多個插槽,請始終為所有的插槽使用完整的基于 <template>
的語法:
<todo-list>
<template v-slot:default="slotProps">
<i class="fas fa-check"></i>
<span class="green">{{ slotProps.item }}</span>
</template>
<template v-slot:other="otherSlotProps">
...
</template>
</todo-list>
作用域插槽的內(nèi)部工作原理是將你的插槽內(nèi)容包括在一個傳入單個參數(shù)的函數(shù)里:
function (slotProps) {
// ... 插槽內(nèi)容 ...
}
這意味著 v-slot
的值實際上可以是任何能夠作為函數(shù)定義中的參數(shù)的 JavaScript 表達(dá)式。你也可以使用 ES2015 解構(gòu)來傳入具體的插槽 prop,如下:
<todo-list v-slot="{ item }">
<i class="fas fa-check"></i>
<span class="green">{{ item }}</span>
</todo-list>
這樣可以使模板更簡潔,尤其是在該插槽提供了多個 prop 的時候。它同樣開啟了 prop 重命名等其它可能,例如將 item
重命名為 todo
:
<todo-list v-slot="{ item: todo }">
<i class="fas fa-check"></i>
<span class="green">{{ todo }}</span>
</todo-list>
你甚至可以定義后備內(nèi)容,用于插槽 prop 是 undefined 的情形:
<todo-list v-slot="{ item = 'Placeholder' }">
<i class="fas fa-check"></i>
<span class="green">{{ item }}</span>
</todo-list>
動態(tài)指令參數(shù)也可以用在 v-slot
上,來定義動態(tài)的插槽名:
<base-layout>
<template v-slot:[dynamicSlotName]>
...
</template>
</base-layout>
跟 v-on
和 v-bind
一樣,v-slot
也有縮寫,即把參數(shù)之前的所有內(nèi)容 (v-slot:
) 替換為字符 #
。例如 v-slot:header
可以被重寫為 #header
:
<base-layout>
<template #header>
<h1>Here might be a page title</h1>
</template>
<template #default>
<p>A paragraph for the main content.</p>
<p>And another one.</p>
</template>
<template #footer>
<p>Here's some contact info</p>
</template>
</base-layout>
然而,和其它指令一樣,該縮寫只在其有參數(shù)的時候才可用。這意味著以下語法是無效的:
<!-- This will trigger a warning -->
<todo-list #="{ item }">
<i class="fas fa-check"></i>
<span class="green">{{ item }}</span>
</todo-list>
如果你希望使用縮寫的話,你必須始終以明確插槽名取而代之:
<todo-list #default="{ item }">
<i class="fas fa-check"></i>
<span class="green">{{ item }}</span>
</todo-list>
更多建議: