Skip to content

Commit 70c7b63

Browse files
committed
2.6: update slot guide
1 parent 535a48d commit 70c7b63

File tree

1 file changed

+235
-62
lines changed

1 file changed

+235
-62
lines changed

src/v2/guide/components-slots.md

Lines changed: 235 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,11 @@ order: 104
66

77
> This page assumes you've already read the [Components Basics](components.html). Read that first if you are new to components.
88
9+
> In 2.6.0, we introduced a new unified syntax (the `v-slot` directive) for named slots and scoped slots. It replaces the `slot` and `slot-scope` syntax which is still documented [here](#Legacy-Syntax). The rationale for introducing the new syntax is described in this [RFC](https://github.com/vuejs/rfcs/blob/master/active-rfcs/0001-new-slot-syntax.md).
10+
911
## Slot Content
1012

11-
Vue implements a content distribution API that's modeled after the current [Web Components spec draft](https://github.com/w3c/webcomponents/blob/gh-pages/proposals/Slots-Proposal.md), using the `<slot>` element to serve as distribution outlets for content.
13+
Vue implements a content distribution API inspired by the [Web Components spec draft](https://github.com/w3c/webcomponents/blob/gh-pages/proposals/Slots-Proposal.md), using the `<slot>` element to serve as distribution outlets for content.
1214

1315
This allows you to compose components like this:
1416

@@ -51,6 +53,34 @@ Or even other components:
5153

5254
If `<navigation-link>` did **not** contain a `<slot>` element, any content passed to it would simply be discarded.
5355

56+
## Compilation Scope
57+
58+
When you want to use data inside a slot, such as in:
59+
60+
``` html
61+
<navigation-link url="/profile">
62+
Logged in as {{ user.name }}
63+
</navigation-link>
64+
```
65+
66+
That slot has access to the same instance properties (i.e. the same "scope") as the rest of the template. The slot does **not** have access to `<navigation-link>`'s scope. For example, trying to access `url` would not work. As a rule, remember that:
67+
68+
> Everything in the parent template is compiled in parent scope; everything in the child template is compiled in the child scope.
69+
70+
## Fallback Content
71+
72+
There are cases when it's useful to provide a slot with fallback content, which is rendered when the parent did not provide content for the slot. For example, a `<submit-button>` component might want the content of the button to be "Submit" by default, but also allow users to override with "Save", "Upload", or anything else.
73+
74+
To achieve this, specify the fallback content in between the `<slot>` tags in your component's template:
75+
76+
```html
77+
<button type="submit">
78+
<slot>Submit</slot>
79+
</button>
80+
```
81+
82+
The fallback content "Submit" only gets rendered if the parent did not provide any slot content.
83+
5484
## Named Slots
5585

5686
There are times when it's useful to have multiple slots. For example, in a hypothetical `base-layout` component with the following template:
@@ -85,145 +115,288 @@ For these cases, the `<slot>` element has a special attribute, `name`, which can
85115
</div>
86116
```
87117

88-
To provide content to named slots, we can use the `slot` attribute on a `<template>` element in the parent:
118+
A `<slot>` outlet without `name` implicitly has the name "default".
119+
120+
To provide content to named slots, we can use the `v-slot` directive on a `<template>` element in the parent, using the directive argument to indicate which named slot the content is being passed to:
89121

90122
```html
91123
<base-layout>
92-
<template slot="header">
124+
<template v-slot:header>
93125
<h1>Here might be a page title</h1>
94126
</template>
95127

96-
<p>A paragraph for the main content.</p>
97-
<p>And another one.</p>
128+
<template v-slot:default>
129+
<p>A paragraph for the main content.</p>
130+
<p>And another one.</p>
131+
</template>
98132

99-
<template slot="footer">
133+
<template v-slot:footer>
100134
<p>Here's some contact info</p>
101135
</template>
102136
</base-layout>
103137
```
104138

105-
Or, the `slot` attribute can also be used directly on a normal element:
139+
Note that unlike the legacy `slot` attribute, `v-slot` must appear on a `<template>` wrapper when being used to denote named slots. The only exception is when used directly on a component tag to denote a [default slot with props](#Default-Slot-with-Props).
106140

107-
``` html
141+
### Named Slots Shorthand
142+
143+
Similar to `v-on` and `v-bind`, `v-slot` also has a shorthand, replacing everything before the argument with the special symbol `#`. The above example can also be written as:
144+
145+
```html
108146
<base-layout>
109-
<h1 slot="header">Here might be a page title</h1>
147+
<template #header>
148+
<h1>Here might be a page title</h1>
149+
</template>
110150

111-
<p>A paragraph for the main content.</p>
112-
<p>And another one.</p>
151+
<template #default>
152+
<p>A paragraph for the main content.</p>
153+
<p>And another one.</p>
154+
</template>
113155

114-
<p slot="footer">Here's some contact info</p>
156+
<template #footer>
157+
<p>Here's some contact info</p>
158+
</template>
115159
</base-layout>
116160
```
117161

118-
There can still be one unnamed slot, which is the **default slot** that serves as a catch-all outlet for any unmatched content. In both examples above, the rendered HTML would be:
162+
### Dynamic Slot Names
163+
164+
Dynamic directive arguments also work on `v-slot`:
119165

120166
``` html
121-
<div class="container">
122-
<header>
123-
<h1>Here might be a page title</h1>
124-
</header>
125-
<main>
126-
<p>A paragraph for the main content.</p>
127-
<p>And another one.</p>
128-
</main>
129-
<footer>
130-
<p>Here's some contact info</p>
131-
</footer>
132-
</div>
167+
<base-layout>
168+
<template v-slot:[dynamicSlotName]>
169+
...
170+
</template>
171+
172+
<!-- shorthand equivalent -->
173+
<template #[anotherSlotName]>
174+
...
175+
</template>
176+
</base-layout>
133177
```
134178

135-
## Default Slot Content
179+
## Slot Props
136180

137-
There are cases when it's useful to provide a slot with default content. For example, a `<submit-button>` component might want the content of the button to be "Submit" by default, but also allow users to override with "Save", "Upload", or anything else.
181+
So far we have seen that the slot feature allows a child component to compose content passed from the parent with its own template. However, in some cases the child component may wish to customize the slot content based on its own data, which the parent has no access to. It would be great if the child can pass props to `<slot>` outlets. Imagine we have a `<slot-example>` component with the following template:
138182

139-
To achieve this, specify the default content in between the `<slot>` tags in your component's template:
183+
``` html
184+
<!-- slot-example -->
185+
<span>
186+
<slot v-bind:msg="myMessage"></slot>
187+
</span>
188+
```
140189

141-
```html
142-
<button type="submit">
143-
<slot>Submit</slot>
144-
</button>
190+
In order to receive and make use of the props being passed to a slot, the parent component declares the props received using the attribute value of the `v-slot` directive:
191+
192+
``` html
193+
<slot-example>
194+
<template v-slot:default="slotProps">
195+
{{ slotProps.msg }}
196+
</template>
197+
</slot-example>
145198
```
146199

147-
If the slot is provided content by the parent, it will replace the default content.
200+
Here you can name `slotProps` anything you like, just like declaring a function argument in JavaScript. The declared variable will become available inside the scope of the slot's `<template>`.
148201

149-
## Compilation Scope
202+
Slots that expect props are also known as "scoped slots", as they create a "scope" within which the received props can be used.
150203

151-
When you want to use data inside a slot, such as in:
204+
<p class="tip">The `v-slot` syntax is introduced in 2.6.0 and is not supported in older versions. The legacy `slot` and `slot-scope` syntax is still supported in 2.6, and is documented [here](#Legacy-Syntax).</p>
205+
206+
### Default Slot with Props
207+
208+
When there is only the default slot, the `v-slot` directive can be placed directly on the component itself, and the `default` argument can also be omitted. The above example can thus be simplified to:
152209

153210
``` html
154-
<navigation-link url="/profile">
155-
Logged in as {{ user.name }}
156-
</navigation-link>
211+
<slot-example v-slot="slotProps">
212+
{{ slotProps.msg }}
213+
</slot-example>
157214
```
158215

159-
That slot has access to the same instance properties (i.e. the same "scope") as the rest of the template. The slot does **not** have access to `<navigation-link>`'s scope. For example, trying to access `url` would not work. As a rule, remember that:
216+
### Slot Props Destructuring
160217

161-
> Everything in the parent template is compiled in parent scope; everything in the child template is compiled in the child scope.
218+
The value of `v-slot` can actually accept any valid JavaScript expression that can appear in the argument position of a function definition. This means in supported environments ([single-file components](single-file-components.html) or [modern browsers](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment#Browser_compatibility)) you can also use [ES2015 destructuring](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment#Object_destructuring) in the expression, like so:
162219

163-
## Scoped Slots
220+
``` html
221+
<slot-example v-slot="{ msg }">
222+
{{ msg }}
223+
</slot-example>
224+
```
225+
226+
This can make the template much cleaner when the slot expects a lot of props.
227+
228+
Conceptually, the syntax is similar to argument destructuring in JavaScript:
229+
230+
``` js
231+
function CustomText({ text }) {
232+
return text
233+
}
234+
```
164235

165-
> New in 2.1.0+
236+
### Todo List Example
166237

167-
Sometimes you'll want to provide a component with a reusable slot that can access data from the child component. For example, a simple `<todo-list>` component may contain the following in its template:
238+
**Slot props allow us to turn slots into reusable templates that can render different content based on input props.** This is most useful when you are designing a reusable component that encapsulates data logic while allowing the consuming parent component to customize part of its layout.
239+
240+
For example, we are implementing a `<todo-list>` component that contains the layout and filtering logic for a list:
168241

169242
```html
170243
<ul>
171244
<li
172-
v-for="todo in todos"
245+
v-for="todo in filteredTodos"
173246
v-bind:key="todo.id"
174247
>
175248
{{ todo.text }}
176249
</li>
177250
</ul>
178251
```
179252

180-
But in some parts of our app, we want the individual todo items to render something different than just the `todo.text`. This is where scoped slots come in.
181-
182-
To make the feature possible, all we have to do is wrap the todo item content in a `<slot>` element, then pass the slot any data relevant to its context: in this case, the `todo` object:
253+
Instead of hard-coding how each todo looks like, we can let the parent component take control by making each todo a slot, and passing in the `todo` as a slot prop:
183254

184255
```html
185256
<ul>
186257
<li
187-
v-for="todo in todos"
258+
v-for="todo in filteredTodos"
188259
v-bind:key="todo.id"
189260
>
190261
<!-- We have a slot for each todo, passing it the -->
191262
<!-- `todo` object as a slot prop. -->
192-
<slot v-bind:todo="todo">
263+
<slot name="todo" v-bind:todo="todo">
193264
<!-- Fallback content -->
194265
{{ todo.text }}
195266
</slot>
196267
</li>
197268
</ul>
198269
```
199270

200-
Now when we use the `<todo-list>` component, we can optionally define an alternative `<template>` for todo items, but with access to data from the child via the `slot-scope` attribute:
271+
Now when we use the `<todo-list>` component, we can optionally define an alternative `<template>` for todo items, but with access to data from the child:
201272

202273
```html
203274
<todo-list v-bind:todos="todos">
204-
<!-- Define `slotProps` as the name of our slot scope -->
205-
<template slot-scope="slotProps">
206-
<!-- Define a custom template for todo items, using -->
207-
<!-- `slotProps` to customize each todo. -->
208-
<span v-if="slotProps.todo.isComplete">✓</span>
209-
{{ slotProps.todo.text }}
275+
<template v-slot:todo="{ todo }">
276+
<span v-if="todo.isComplete">✓</span>
277+
{{ todo.text }}
210278
</template>
211279
</todo-list>
212280
```
213281

214-
> In 2.5.0+, `slot-scope` is no longer limited to the `<template>` element, but can instead be used on any element or component in the slot.
282+
Admitted, the above example is still contrived. A more real-world use case would be a reusable list component that encapsulates the logic of fetching items from an API with infinite scroll, and lets the parent customize how each fetched item should be rendered:
215283

216-
### Destructuring `slot-scope`
284+
``` html
285+
<infinite-scroll endpoint="/api/posts" :fetch-count="20">
286+
<template v-slot:item="{ data }">
287+
<div class="post">
288+
<h2>{{ data.title }}</h2>
289+
<p>by {{ data.author }} on {{ data.date }}</p>
290+
<p>{{ data.content }}</p>
291+
</div>
292+
</template>
293+
</infinite-scroll>
294+
```
295+
296+
## Legacy Syntax
297+
298+
The `v-slot` directive was introduced in Vue 2.6.0, replacing the legacy `slot` + `slot-scope` syntax. The rationale for introducing the new syntax is described in this [RFC](https://github.com/vuejs/rfcs/blob/master/active-rfcs/0001-new-slot-syntax.md). The legacy syntax is still supported in 2.6 and will continue to be supported in all future 2.x releases, but is marked for deprecation and will eventually be removed in a future major release.
299+
300+
### Named Slots with `slot`
217301

218-
The value of `slot-scope` can actually accept any valid JavaScript expression that can appear in the argument position of a function definition. This means in supported environments ([single-file components](single-file-components.html) or [modern browsers](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment#Browser_compatibility)) you can also use [ES2015 destructuring](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment#Object_destructuring) in the expression, like so:
302+
To pass content to named slots from the parent, use the special `slot` attribute on `<template>` (Using the `<base-layout>` component described [here](#Named-Slots) as example):
303+
304+
```html
305+
<base-layout>
306+
<template slot="header">
307+
<h1>Here might be a page title</h1>
308+
</template>
309+
310+
<p>A paragraph for the main content.</p>
311+
<p>And another one.</p>
312+
313+
<template slot="footer">
314+
<p>Here's some contact info</p>
315+
</template>
316+
</base-layout>
317+
```
318+
319+
Or, the `slot` attribute can also be used directly on a normal element:
320+
321+
``` html
322+
<base-layout>
323+
<h1 slot="header">Here might be a page title</h1>
324+
325+
<p>A paragraph for the main content.</p>
326+
<p>And another one.</p>
327+
328+
<p slot="footer">Here's some contact info</p>
329+
</base-layout>
330+
```
331+
332+
There can still be one unnamed slot, which is the **default slot** that serves as a catch-all outlet for any unmatched content. In both examples above, the rendered HTML would be:
333+
334+
``` html
335+
<div class="container">
336+
<header>
337+
<h1>Here might be a page title</h1>
338+
</header>
339+
<main>
340+
<p>A paragraph for the main content.</p>
341+
<p>And another one.</p>
342+
</main>
343+
<footer>
344+
<p>Here's some contact info</p>
345+
</footer>
346+
</div>
347+
```
348+
349+
### Scoped Slots with `slot-scope`
350+
351+
To receive props passed to a slot, the parent component can use `<template>` with the `slot-scope` special attribute (Using the `<slot-example>` described [here](#Slot-Props) as example):
352+
353+
``` html
354+
<slot-example>
355+
<template slot="default" slot-scope="slotProps">
356+
{{ slotProps.msg }}
357+
</template>
358+
</slot-example>
359+
```
360+
361+
Here `slot-scope` declares the received props object as the `slotProps` variable, and makes it available inside the `<template>` scope. You can name `slotProps` anything you like similar to naming function arguments in JavaScript.
362+
363+
Here `slot="default"` can be omitted as it is implied:
364+
365+
``` html
366+
<slot-example>
367+
<template slot-scope="slotProps">
368+
{{ slotProps.msg }}
369+
</template>
370+
</slot-example>
371+
```
372+
373+
`slot-scope` can also be used directly on a non `<template>` element (including components):
374+
375+
``` html
376+
<slot-example>
377+
<span slot-scope="slotProps">
378+
{{ slotProps.msg }}
379+
</span>
380+
</slot-example>
381+
```
382+
383+
The value of `slot-scope` can accept any valid JavaScript expression that can appear in the argument position of a function definition. This means in supported environments ([single-file components](single-file-components.html) or [modern browsers](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment#Browser_compatibility)) you can also use [ES2015 destructuring](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment#Object_destructuring) in the expression, like so:
384+
385+
``` html
386+
<slot-example>
387+
<span slot-scope="{ msg }">
388+
{{ msg }}
389+
</span>
390+
</slot-example>
391+
```
392+
393+
Using the `<todo-list>` described [here](#Todo-List-Example) as example, here's the equivalent usage using `slot-scope`:
219394

220395
```html
221396
<todo-list v-bind:todos="todos">
222-
<template slot-scope="{ todo }">
397+
<template slot="todo" slot-scope="{ todo }">
223398
<span v-if="todo.isComplete">✓</span>
224399
{{ todo.text }}
225400
</template>
226401
</todo-list>
227402
```
228-
229-
This is a great way to make scoped slots a little cleaner.

0 commit comments

Comments
 (0)