插槽內容
Vue實現一套內容分發的API, 這套API基於當前的web組件規範草案,將<slot>元素作爲承載分發內容的出口。
<navigation-link url="/profile">
Your Profile
</navigation-link>
然後在<navigation-link>的模板中可能會寫爲:
<a :href="url" class="nav-link">
<slot></slot>
</a>
當組件渲染的時候,這個<slot>元素將會被"Yourt Profile"替換。插槽可以包括任何模板代碼,包括HTML:
<navigation-link url="/profile">
<span class="fa fa-user"></span>
Your Profile
</navigation-link>
甚至其他的組件:
<navigation-link url="/profile">
<font-awesome-icon name="user"></font-awesome-icon>
Your Profile
</navigation-link>
如果<navigation-link>沒有包含一個<slot>元素,則任何傳入它的內容都會被拋棄。
具名插槽
有些時候我們需要多個插槽,例如:
<div>
<header>我們希望把頁頭放這裏</header>
<main>我們希望把主要內容放這裏</main>
<footer>我們希望把頁腳放這裏</footer>
</div>
對於這種情況,<slot>元素有一個特殊的特性:name。這個特性可以用來定義額外的插槽:
<div class="container">
<header>
<slot name="header"></slot>
</header>
<main>
<slot></slot>
</main>
<footer>
<slot name="footer"></slot>
</footer>
</div>
在向具名插槽提供內容的時候,我們可以在一個父組件的<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 is some contact info</p>
</template>
</base-layout>
另一種slot特性的用法是直接用在一個普通元素上:
<base-layout>
<h1 slot="header">Here might be a page title</h1>
<p>A paragraph for the main content</p>
<p>And another one</p>
<p slot="footer">Here is some contact info</p>
</base-layout>
我們還是可以保留一個未命名插槽,這個插槽是默認插槽,也就是說它會作爲所有未匹配到插槽的內容的統一出口,上述兩個示例渲染出來的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>
插槽的默認內容
有的時候爲插槽提供默認內容是很有用的。例如,一個<submit-button>組件可能希望這個按鈕的默認內容是"Submit",但是同時允許用戶覆蓋"Save","Upload"或者別的內容。
你可以在組件模板裏的<slot>標籤內部指定默認的內容來做到這一點。
<button type="submit">
<slot>Submit</slot>
</button>
如果父組件爲這個插槽提供內容,則默認內容會被替換。
編譯作用域
當你想在插槽內使用數據時,例如:
<navigation-link url="/profile">
Logged in as {{ user.name }}
</navigation-link>
該插槽可以訪問跟這個模板的其他地方相同的實例屬性(也就是說作用域相同)。但這個插槽不能訪問<navigation-link>的作用域。例如嘗試訪問URL是不會工作的。牢記:
>**父組件模板的所有東西都會在父級作用域內編譯;子組件的模板的所有東西都會在子級作用域內編譯。**
作用域插槽
有時候你希望提供的組件帶有一個可從子組件獲取數據的可複用的插槽:
<ul>
<li v-for="todo in todos" v-bind="todo.id">
{{ todo.text }}
</li>
</ul>
但是在我們應用的某些部分,我們希望每個獨立的代辦項渲染出和todo.text不太一樣的東西。這也是作用域插槽的用武之地。
爲了讓這個特性成爲可能,你需要做的全部事情就是將代辦項內容包裹在一個<slot>元素上,然後將所有和其上下文有關的數據傳遞給這個插槽:在這個例子中,這個數據是todo對象:
<ul>
<li v-for="todo in todos" :key="todo.id">
<slot :todo="todo">{{ todo.text }}</slot>
</li>
</ul>
現在當我們使用<todo-list>組件的適合,我們可以選擇爲代碼項定義一個不一樣的<template>作爲替代方案,並且可以通過slot-scope特性從子組件獲取數據:
<todo-list :todos="todos">
<!-- 將 `slotProps` 定義爲插槽作用域的名字 -->
<template slot-scope="slotProps">
<!-- 爲待辦項自定義一個模板,-->
<!-- 通過 `slotProps` 定製每個待辦項。-->
<span v-if="slotProps.todo.is">good</span>
{{ slotProps.todo.text }}
</template>
</todo-list>
解構 slot-scope
如果一個JavaScript表達式在一個函數定義的參數位置有效,那麼這個表達式實際上就可以被slot-scope接受。也就是說你可以在支持的環境下,在這些表達式中使用ES2015解構語法。
<todo-list :todos="todos">
<template slot-scope="{ todo }">
<span v-if="todo.isComplete">✓</span>
{{ todo.text }}
</template>
</todo-list>