Slots

Esta página assume que você já leu o Básico sobre Componentes. Leia lá primeiro se você for novo com componentes.

Na versão 2.6, Nós introduzimos uma nova sintaxe unificada (A diretiva v-slot) para os slots nomeados e com escopo. Ele substitui os atributos slot e slot-scope, os quais estão agora obsoletos, mas não foram removidos e ainda estão documentados aqui. Os fundamentos para a nova sintaxe estão descritos neste RFC.

Conteúdo do Slot

Vue implementa uma API de distribuição de conteúdo que é modelada após o atual detalhamento da especificação dos componentes da Web, usando o elemento <slot> para servir como saída de distribuição de conteúdos.

Isso permite que você componha componentes como esse:

<navigation-link url="/profile">
Seu Perfil
</navigation-link>

Então no template para <navigation-link>, você poderá ter:

<a
v-bind:href="url"
class="nav-link"
>
<slot></slot>
</a>

Quando o componente renderizar, o elemento <slot> será substituido por “Seu Perfil”. Slots podem conter qualquer tipo de código template, incluindo HTML:

<navigation-link url="/profile">
<!-- Adicionando ícones Font Awesome -->
<span class="fa fa-user"></span>
Seu Perfil
</navigation-link>

Ou até mesmo outros componentes:

<navigation-link url="/profile">
<!-- Usando um componente para adicionar um ícone -->
<font-awesome-icon name="user"></font-awesome-icon>
Seu Perfil
</navigation-link>

Se no <navigation-link> não contém um elemento <slot>, qualquer conteúdo passado para ele simplesmente será descartado.

Escopo de compilação

Quando você quer usar um dado dentro de um slot, como em:

<navigation-link url="/profile">
Logado como {{ user.name }}
</navigation-link>

Esse slot tem acesso as mesmas propriedades da instância (i.e. ao mesmo “escopo”) como o resto do template. O slot não tem acesso ao escopo do <navigation-link>. Por exemplo, tentando acessar a url não funcionaria:

<navigation-link url="/profile">
Clicando aqui te enviará para: {{ url }}
<!--
O `url` será undefined, porque esse conteúdo é passado
para <navigation-link>, em vez de definido dentro do componente <navigation-link>.
-->
</navigation-link>

Como regra, lembre-se de que:

Tudo no template pai é compilado no escopo do pai; Tudo no template filho é compilado no escopo do filho.

Conteúdo de Fallback

Há casos em que é útil especificar o conteúdo de fallback (i.e. padrão) para um slot, a ser renderizado somente quando nenhum conteúdo é fornecido. Por exemplo, no componente <submit-button>:

<button type="submit">
<slot></slot>
</button>

Podemos querer que o texto “Enviar” seja renderizado dentro do <button> na maioria das vezes. para “Enviar” o conteúdo de fallback, podemos colocá-lo entre as tags <slot>:

<button type="submit">
<slot>Enviar</slot>
</button>

Agora, quando usamos <submit-button> no componente pai, sem fornecer conteúdo para o slot:

<submit-button></submit-button>

Irá renderizar o conteúdo de fallback, “Enviar”:

<button type="submit">
Enviar
</button>

Mas se nós fornecermos conteúdo:

<submit-button>
**Salvar**
</submit-button>

Então, o conteúdo fornecido será renderizado:

<button type="submit">
Salvar
</button>

Slots Nomeados

Atualizado em 2.6.0+. Veja aqui para a sintaxe obsoleta usando o atributo slot.

Há momentos em que é útil ter múltiplos elementos slots. Por exemplo, em um componente <base-layout> com o seguinte template:

<div class="container">
<header>
<!-- Nós queremos o cabeçalho aqui -->
</header>
<main>
<!-- Nós queremos o conteúdo principal aqui -->
</main>
<footer>
<!-- Nós queremos o rodapé aqui -->
</footer>
</div>

Para estes casos, o elemento <slot> tem um atributo especial chamado, name, que pode ser usado para definir slots adicionais:

<div class="container">
<header>
<slot name="header"></slot>
</header>
<main>
<slot></slot>
</main>
<footer>
<slot name="footer"></slot>
</footer>
</div>

Uma saída <slot> sem name tem implicitamente o nome “default”.

Para fornecer conteúdo para slots nomeados, nós podemos usar a diretiva v-slot em um <template>, fornecendo o nome do slot como argumento do v-slot:

<base-layout>
<template v-slot:header>
<h1>Aqui pode estar um título da página</h1>
</template>

<p>Um parágrafo para o conteúdo principal.</p>
<p>E um outro parágrafo.</p>

<template v-slot:footer>
<p>Aqui estão algumas informações de contato</p>
</template>
</base-layout>

Agora tudo dentro dos elementos <template> serão passado aos slots correspondentes. Qualquer conteúdo não envolvido por um <template> usando v-slot é assumido como sendo o slot default.

No entanto, você ainda pode incluir o conteúdo do slot default em um <template> se você deseja ser explícito:

<base-layout>
<template v-slot:header>
<h1>Aqui pode estar um título da página</h1>
</template>

<template v-slot:default>
<p>Um parágrafo para o conteúdo principal.</p>
<p>E um outro parágrafo.</p>
</template>

<template v-slot:footer>
<p>Aqui estão algumas informações de contato</p>
</template>
</base-layout>

De qualquer forma, O HTML renderizado será:

<div class="container">
<header>
<h1>Aqui pode estar um título da página</h1>
</header>
<main>
<p>Um parágrafo para o conteúdo principal.</p>
<p>E um outro parágrafo.</p>
</main>
<footer>
<p>Aqui estão algumas informações de contato</p>
</footer>
</div>

Observe que v-slot só pode ser adicionado a um <template> (com única exceção), ao contrário dos atributos slot.

Slots com Escopo Definido

Atualizado em 2.6.0+. Veja aqui para a sintaxe obsoleta usando o atributo slot-scope.

As vezes, é útil que o conteúdo do slot tenha acesso aos dados disponíveis apenas no componente filho. Por exemplo, imagine um componente <current-user> com o seguinte template:

<span>
<slot>{{ user.lastName }}</slot>
</span>

Podemos querer substituir esse conteúdo de fallback para exibir o primeiro nome do usuário, em vez do último, assim:

<current-user>
{{ user.firstName }}
</current-user>

Isso não funcionará, no entanto, porque somente o componente <current-user> tem acesso ao user e o conteúdo que estamos fornecendo é renderizado no pai.

Para tornar user disponível para o conteúdo do slot no pai, podemos vincular user como um atributo do elemento <slot>:

<span>
<slot v-bind:user="user">
{{ user.lastName }}
</slot>
</span>

Atributos vinculados a um elemento <slot> são chamados de props do slot. Agora, no escopo pai, podemos usar o v-slot com um valor para definir um nome para o slot que nos foi fornecido:

<current-user>
<template v-slot:default="slotProps">
{{ slotProps.user.firstName }}
</template>
</current-user>

Neste exemplo, escolhemos nomear o objeto que contém todos os nossos props do slot slotProps, mas você pode usar qualquer nome que desejar.

Sintaxe abreviada para slots default

Em casos como acima, quando apenas o slot default é fornecido, as tags do componente podem ser usadas como o template. Isso nos permite usar o v-slot diretamente no componente:

<current-user v-slot:default="slotProps">
{{ slotProps.user.firstName }}
</current-user>

Isso pode ser encurtado ainda mais. Assim como o conteúdo não especificado é considerado como sendo o slot default, assume-se que o v-slot sem um argumento se refira ao slot default:

<current-user v-slot="slotProps">
{{ slotProps.user.firstName }}
</current-user>

Observe que a sintaxe abreviada para o slot default não pode ser misturada com slots nomeados, pois isso levaria a ambiguidade do escopo:

<!-- inválido, resultará em aviso -->
<current-user v-slot="slotProps">
{{ slotProps.user.firstName }}
<template v-slot:other="otherSlotProps">
slotProps não está disponível aqui!
</template>
</current-user>

Sempre que houver vários slots, use a sintaxe completa com base em <template> para todos os slots:

<current-user>
<template v-slot:default="slotProps">
{{ slotProps.user.firstName }}
</template>

<template v-slot:other="otherSlotProps">
...
</template>
</current-user>

Desestruturando Props do Slot

Internamente, os slots com escopo trabalham envolvendo seu conteúdo em uma função passando um único argumento:

function (slotProps) {
// ... conteúdo do slot ...
}

Isso significa que o valor de v-slot pode realmente aceitar qualquer expressão JavaScript válida que possa aparecer na posição de argumento em uma definição de função. Então, em ambientes suportados (componentes single-file ou browsers modernos), você também pode usar desestruturação de objetos do ES2015 para usar props específicos de slots, assim:

<current-user v-slot="{ user }">
{{ user.firstName }}
</current-user>

Isso pode tornar o template bem mais limpo, especialmente quando o slot fornece muitas props. Também abre outras possibilidades, tais como renomear props, e.g. user para person:

<current-user v-slot="{ user: person }">
{{ person.firstName }}
</current-user>

Você pode até mesmo definir fallbacks, para serem usados no caso de um prop do slot ser undefined:

<current-user v-slot="{ user = { firstName: 'Guest' } }">
{{ user.firstName }}
</current-user>

Slots de nomes dinâmicos

Novo na versão 2.6.0+

Argumentos de diretiva dinâmicos também funciona no v-slot, permitindo a definição de nomes de slots dinâmicos:

<base-layout>
<template v-slot:[dynamicSlotName]>
...
</template>
</base-layout>

Forma Abreviada para Slots Nomeados

Novo na versão 2.6.0+

Semelhante ao v-on e v-bind, v-slot também possui uma forma abreviada, substituindo tudo antes do argumento (v-slot:) pelo símbolo especial #. Por exemplo, v-slot:header pode ser reescrito como #header:

<base-layout>
<template #header>
<h1>Aqui pode estar um título da página</h1>
</template>

<p>Um parágrafo para o conteúdo principal.</p>
<p>E um outro parágrafo.</p>

<template #footer>
<p>Aqui estão algumas informações de contato</p>
</template>
</base-layout>

No entanto, assim como com outras diretivas, a abreviação só está disponível quando um argumento é fornecido. Isso significa que a seguinte sintaxe é inválida:

<!-- Isso acionará um aviso -->
<current-user #="{ user }">
{{ user.firstName }}
</current-user>

Em vez disso, você sempre deve especificar um nome para o slot se desejar usar o atalho:

<current-user #default="{ user }">
{{ user.firstName }}
</current-user>

Outros exemplos

Props de slot nos permitem transformar slots em templates reutilizáveis que podem renderizar diferentes conteúdos com base em adições de entrada. Isso é mais útil quando você está projetando um componente reutilizável que encapsula a lógica de dados, enquanto permite que o componente pai consumidor personalize parte de seu layout.

Por exemplo, estamos implementando um componente <todo-list> que contém o layout e a lógica de filtragem para uma lista:

<ul>
<li
v-for="todo in filteredTodos"
v-bind:key="todo.id"
>
{{ todo.text }}
</li>
</ul>

Em vez de codificar o conteúdo para cada todo, podemos deixar que o componente pai assuma o controle fazendo de todos todo um slot, então ligando todo como uma prop de slot:

<ul>
<li
v-for="todo in filteredTodos"
v-bind:key="todo.id"
>
<!--
Nós temos um slot para cada _todo_ passando o objeto
`todo` como uma prop de slot.
-->
<slot name="todo" v-bind:todo="todo">
<!-- conteúdo fallback -->
{{ todo.text }}
</slot>
</li>
</ul>

Agora, quando usamos o componente <todo-list>, podemos opcionalmente definir um <template> alternativo para itens do todo, mas com acesso aos dados do filho:

<todo-list v-bind:todos="todos">
<template v-slot:todo="{ todo }">
<span v-if="todo.isComplete"></span>
{{ todo.text }}
</template>
</todo-list>

No entanto, mesmo isso apenas arranha a superfície do que slots com escopo são capazes de fazer. Para exemplos completos e reais de uso, recomendamos buscar mais sobre bibliotecas como Vue Virtual Scroller, Vue Promised, e Portal Vue.

Sintaxe Obsoleta

A diretiva v-slot foi introduzida no Vue 2.6, oferecendo uma API melhorada e alternativa aos atributos slot e slot-scope ainda suportados. O raciocínio completo para introduzir o v-slot é descrito neste RFC. Os atributos slot e slot-scope continuarão a ser suportados em todas as futuras versões 2.X, mas serão oficialmente descontinuados e serão eventualmente removidos no Vue 3.

Slots Nomeados com o atributo slot

Obsoleta no 2.6.0+. Veja aqui a nova e recomendada sintaxe.

Para passar conteúdos para os slots nomeados a partir do pai, use o atributo especial slot no <template> (usando o componente <base-layout> descrito aqui como exemplo):

<base-layout>
<template slot="header">
<h1>Aqui pode ser o título da página</h1>
</template>

<p>Um parágrafo para o conteúdo principal.</p>
<p>E um outro parágrafo.</p>

<template slot="footer">
<p>Aqui algumas informações de contato</p>
</template>
</base-layout>

Ou o atributo slot que também pode ser usado diretamente em um elemento normal:

<base-layout>
<h1 slot="header">Aqui pode ser o título da página</h1>

<p>Um parágrafo para o conteúdo principal.</p>
<p>E um outro parágrafo.</p>

<p slot="footer">Aqui algumas informações de contato</p>
</base-layout>

Ainda pode haver um slot sem nome, que é o slot default que serve como um agrupador para qualquer conteúdo que esteja solto. Em ambos exemplos acima, o HTML renderizado seria:

<div class="container">
<header>
<h1>Aqui pode ser o título da página</h1>
</header>
<main>
<p>Um parágrafo para o conteúdo principal.</p>
<p>E um outro parágrafo.</p>
</main>
<footer>
<p>Aqui algumas informações de contato</p>
</footer>
</div>

Slots de Escopo Definido com o atributo slot-scope

Obsoleta no 2.6.0+. Veja aqui a nova e recomendada sintaxe.

Para receber as props passadas para um slot, o componente pai pode usar o <template> com o atributo slot-scope (usando o <slot-exemplo> descrito no exemplo aqui):

<slot-example>
<template slot="default" slot-scope="slotProps">
{{ slotProps.msg }}
</template>
</slot-example>

Aqui, o slot-scope declara o objeto props recebido como a variável slotProps, e o torna disponível dentro do escopo do <template>. Você pode nomear o slotProps da maneira que preferir, semelhante a argumentos de funções em JavaScript.

Aqui slot=default pode ser omitido como está implicitado:

<slot-example>
<template slot-scope="slotProps">
{{ slotProps.msg }}
</template>
</slot-example>

O atributo slot-scope também pode ser usado diretamente em elementos que não sejam <template> (incluindo componentes):

<slot-example>
<span slot-scope="slotProps">
{{ slotProps.msg }}
</span>
</slot-example>

O valor de slot-scope pode aceitar qualquer expressão JavaScript válida que pode aparecer na posição de argumento de uma definição de função. Isso significa que em ambientes suportados (single-file components ou browsers modernos) você também pode usar desestruturação de objetos do ES2015 na expressão, da seguinte forma:

<slot-example>
<span slot-scope="{ msg }">
{{ msg }}
</span>
</slot-example>

Usando o <todo-list> descrito aqui como um exemplo, aqui está o uso equivalente usando slot-scope:

<todo-list v-bind:todos="todos">
<template slot="todo" slot-scope="{ todo }">
<span v-if="todo.isComplete"></span>
{{ todo.text }}
</template>
</todo-list>