> 該頁面假設你已經閱讀過了[組件基礎](818090.md)。如果你還對組件不太了解,推薦你先閱讀它。
## 插槽內容
Vue 實現了一套內容分發的 API,這套 API 基于當前的 [Web Components 規范草案](https://github.com/w3c/webcomponents/blob/gh-pages/proposals/Slots-Proposal.md),將 `<slot>` 元素作為承載分發內容的出口。
它允許你像這樣合成組件:
``` html
<navigation-link url="/profile">
Your Profile
</navigation-link>
```
然后你在 `<navigation-link>` 的模板中可能會寫為:
``` html
<a
v-bind:href="url"
class="nav-link"
>
<slot></slot>
</a>
```
當組件渲染的時候,這個 `<slot>` 元素將會被替換為“Your Profile”。插槽內可以包含任何模板代碼,包括 HTML:
``` html
<navigation-link url="/profile">
<!-- 添加一個 Font Awesome 圖標 -->
<span class="fa fa-user"></span>
Your Profile
</navigation-link>
```
甚至其它的組件:
``` html
<navigation-link url="/profile">
<!-- 添加一個圖標的組件 -->
<font-awesome-icon name="user"></font-awesome-icon>
Your Profile
</navigation-link>
```
如果 `<navigation-link>` **沒有**包含一個 `<slot>` 元素,則任何傳入它的內容都會被拋棄。
## 具名插槽
有些時候我們需要多個插槽。例如,一個假設的 `<base-layout>` 組件的模板如下:
``` html
<div class="container">
<header>
<!-- 我們希望把頁頭放這里 -->
</header>
<main>
<!-- 我們希望把主要內容放這里 -->
</main>
<footer>
<!-- 我們希望把頁腳放這里 -->
</footer>
</div>
```
對于這樣的情況,`<slot>` 元素有一個特殊的特性:`name`。這個特性可以用來定義額外的插槽:
``` html
<div class="container">
<header>
<slot name="header"></slot>
</header>
<main>
<slot></slot>
</main>
<footer>
<slot name="footer"></slot>
</footer>
</div>
```
在向具名插槽提供內容的時候,我們可以在一個父組件的 `<template>` 元素上使用 `slot` 特性:
```html
<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>
```
另一種 `slot` 特性的用法是直接用在一個普通的元素上:
``` html
<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's some contact info</p>
</base-layout>
```
我們還是可以保留一個未命名插槽,這個插槽是**默認插槽**,也就是說它會作為所有未匹配到插槽的內容的統一出口。上述兩個示例渲染出來的 HTML 都將會是:
``` 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>` 標簽內部指定默認的內容來做到這一點。
```html
<button type="submit">
<slot>Submit</slot>
</button>
```
如果父組件為這個插槽提供了內容,則默認的內容會被替換掉。
## 編譯作用域
當你想在插槽內使用數據時,例如:
``` html
<navigation-link url="/profile">
Logged in as {{ user.name }}
</navigation-link>
```
該插槽可以訪問跟這個模板的其它地方相同的實例屬性 (也就是說“作用域”是相同的)。但這個插槽**不能**訪問 `<navigation-link>` 的作用域。例如嘗試訪問 `url` 是不會工作的。牢記一條準則:
> 父組件模板的所有東西都會在父級作用域內編譯;子組件模板的所有東西都會在子級作用域內編譯。
## 作用域插槽
> 2.1.0+ 新增
有的時候你希望提供的組件帶有一個可從子組件獲取數據的可復用的插槽。例如一個簡單的 `<todo-list>` 組件的模板可能包含了如下代碼:
```html
<ul>
<li
v-for="todo in todos"
v-bind:key="todo.id"
>
{{ todo.text }}
</li>
</ul>
```
但是在我們應用的某些部分,我們希望每個獨立的待辦項渲染出和 `todo.text` 不太一樣的東西。這也是作用域插槽的用武之地。
為了讓這個特性成為可能,你需要做的全部事情就是將待辦項內容包裹在一個 `<slot>` 元素上,然后將所有和其上下文相關的數據傳遞給這個插槽:在這個例子中,這個數據是 `todo` 對象:
```html
<ul>
<li
v-for="todo in todos"
v-bind:key="todo.id"
>
<!-- 我們為每個 todo 準備了一個插槽,-->
<!-- 將 `todo` 對象作為一個插槽的 prop 傳入。-->
<slot v-bind:todo="todo">
<!-- 回退的內容 -->
{{ todo.text }}
</slot>
</li>
</ul>
```
現在當我們使用 `<todo-list>` 組件的時候,我們可以選擇為待辦項定義一個不一樣的 `<template>` 作為替代方案,并且可以通過 `slot-scope` 特性從子組件獲取數據:
```html
<todo-list v-bind:todos="todos">
<!-- 將 `slotProps` 定義為插槽作用域的名字 -->
<template slot-scope="slotProps">
<!-- 為待辦項自定義一個模板,-->
<!-- 通過 `slotProps` 定制每個待辦項。-->
<span v-if="slotProps.todo.isComplete">?</span>
{{ slotProps.todo.text }}
</template>
</todo-list>
```
> 在 2.5.0+,`slot-scope` 不再限制在 `<template>` 元素上使用,而可以用在插槽內的任何元素或組件上。
### 解構 `slot-scope`
如果一個 JavaScript 表達式在一個函數定義的參數位置有效,那么這個表達式實際上就可以被 `slot-scope` 接受。也就是說你可以在支持的環境下 ([單文件組件](single-file-components.html)或[現代瀏覽器](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment#瀏覽器兼容)),在這些表達式中使用 [ES2015 解構語法](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment#解構對象)。例如:
```html
<todo-list v-bind:todos="todos">
<template slot-scope="{ todo }">
<span v-if="todo.isComplete">?</span>
{{ todo.text }}
</template>
</todo-list>
```
這會使作用域插槽變得更干凈一些。
- 寫在前面
- 基礎
- 安裝
- 介紹
- Vue實例
- 模板語法
- 計算屬性和偵聽器
- Class 與 Style 綁定
- 條件渲染
- 列表渲染
- 事件處理
- 表單輸入綁定
- 組件基礎
- 深入了解組件
- 組件注冊
- Prop
- 自定義事件
- 插槽
- 動態組件 & 異步組件
- 處理邊界情況
- 過渡 & 動畫
- 進入/離開 & 列表過渡
- 狀態過渡
- 可復用性 & 組合
- 混入
- 自定義指令
- 渲染函數 & JSX
- 插件
- 過濾器
- 工具
- 生產環境部署
- 單文件組件
- 單元測試
- TypeScript 支持
- 規模化
- 路由
- 狀態管理
- 服務端渲染
- 內在
- 深入響應式原理
- 遷移
- 從 Vue 1.x 遷移
- 從 Vue Router 0.7.x 遷移
- 從 Vuex 0.6.x 遷移到 1.0
- 更多
- 對比其他框架
- 加入 Vue.js 社區
- 開發團隊