Guia
Essenciais
- Instalação
- Introdução
- A Instância Vue
- Sintaxe de Templates
- Dados Computados e Observadores
- Interligações de Classe e Estilo
- Renderização Condicional
- Renderização de Listas
- Manipulação de Eventos
- Interligações em Formulários
- Básico sobre Componentes
Componentes em Detalhes
- Registro de Componentes
- Propriedades
- Eventos Personalizados
- Slots
- Dinâmicos & Assíncronos
- Lidando com Casos Extremos
Transições & Animações
- Transições de Visibilidade e Listas
- Transições de Estado
Reuso & Composição
- Mixins
- Diretivas Personalizadas
- Funções de Renderização & JSX
- Plugins
- Filtros
Ferramentas
- Componentes Single-File
- Testes Unitários
- Testing
- Suporte ao TypeScript
- Publicando em Produção
Escalonando
- Roteamento
- Gerenciamento de Estado
- Renderizando no Lado do Servidor
- Segurança
Internamente
- Reatividade em Profundidade
Migração
- Migração do Vue 1.x
- Migração do Vue Router 0.7.x
- Migração do Vuex 0.6.x para 1.0
Diversos
- Comparação com Outros Frameworks
- Junte-se à Comunidade Vue.js!
- Conheça a Equipe
Você está navegando a documentação da v2.x e anterior. Para a v3.x, clique aqui.
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 atributosslot
eslot-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, <slot></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 (isto é, 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 (isto é, 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 passados 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 as propriedades slot que fornecemos:
<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 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 na inserção de props. 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 vinculando 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.0, oferecendo uma API melhorada e alternativa aos atributosslot
eslot-scope
ainda suportados. O raciocínio completo para introduzir ov-slot
é descrito neste RFC. Os atributosslot
eslot-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>