Criando Componentes Vue
Um componante Vue é definido em um único arquivo com extensão .vue, contendo template, script e estilos.
Criando e Registrando um Componente
Crie o arquivo src/components/Saudacao.vue:
<template>
<div class="saudacao">
<p>{{ mensagem }}</p>
</div>
</template>
<script>
export default {
name: "Saudacao",
data() {
return {
mensagem: "Olá, bem-vindo!"
}
}
}
</script>
<style scoped>
.saudacao {
font-size: 1.2em;
color: #2c3e50;
}
</style>
O atributo scoped garante que os estilos afetem apenas este componnete.
Para utilizar no src/App.vue:
<template>
<div id="app">
<Saudacao />
</div>
</template>
<script>
import Saudacao from "./components/Saudacao"
export default {
name: 'App',
components: {
Saudacao
}
}
</script>
Passagem de Dados do Pai para o Filho (Props)
O componente pai envia dados ao filho através de atributos no template. O componente filho declara essas propreidades via props.
Exemplo Prático
Crie src/components/CardProduto.vue:
<template>
<div class="card">
<h3>{{ titulo }}</h3>
<span>Código: {{ codigo }}</span>
<p>Descrição: {{ descricao }}</p>
<p>Preço: R$ {{ preco }}</p>
<p>Categoria: {{ categoria }}</p>
</div>
</template>
<script>
export default {
name: 'CardProduto',
props: {
titulo: {
type: String,
required: true
},
codigo: {
type: [String, Number],
required: true
},
descricao: String,
preco: Number,
categoria: {
type: String,
default: "Geral"
}
}
}
</script>
Crie src/components/Vitrine.vue:
<template>
<div class="vitrine">
<h2>Vitrine de Produtos</h2>
<CardProduto
:codigo="produtoAtual.codigo"
:titulo="produtoAtual.nome"
:preco="produtoAtual.valor"
:descricao="produtoAtual.desc"
/>
</div>
</template>
<script>
import CardProduto from "./CardProduto"
export default {
name: 'Vitrine',
components: {
CardProduto
},
data() {
return {
produtoAtual: {
codigo: 101,
nome: "Teclado Mecânico",
valor: 250,
desc: "Switches Cherry MX Blue"
}
}
}
}
</script>
Comunicação do Filho para o Pai ($emit)
O componente filho dispara eventos customizados usando this.$emit(). O pai escuta esses eventos com v-on ou @.
Exemplo Prático
Crie src/components/BotaoAcao.vue:
<template>
<div class="botao-acao">
<button @click="enviarDados">Enviar para o Pai</button>
</div>
</template>
<script>
export default {
name: 'BotaoAcao',
data() {
return {
mensagemInterna: "Dados do componente filho"
}
},
methods: {
enviarDados() {
this.$emit("receberMensagem", this.mensagemInterna)
}
}
}
</script>
Crie src/components/PainelControle.vue:
<template>
<div class="painel">
<h2>Painel de Controle</h2>
<BotaoAcao @receberMensagem="processarMensagem" />
<p v-if="mensagemRecebida">Recebido: {{ mensagemRecebida }}</p>
</div>
</template>
<script>
import BotaoAcao from "./BotaoAcao"
export default {
name: 'PainelControle',
components: {
BotaoAcao
},
data() {
return {
mensagemRecebida: null
}
},
methods: {
processarMensagem(dados) {
this.mensagemRecebida = dados
}
}
}
</script>
Slots no Vue
Slots permitem que o componente pai insira conteúdo dentro da estrutura do componente filho.
Slot Padrão
No componente filho, utilize <slot> como espaço reservado:
<template>
<div class="caixa-conteudo">
<h3>Cabeçalho fixo</h3>
<slot>Conteúdo padrão aqui</slot>
</div>
</template>
Slots Nomeados
Permite múltiplos pontos de inserção de conteúdo:
<template>
<div class="layout">
<header>
<slot name="cabecalho">Header padrão</slot>
</header>
<main>
<slot></slot>
</main>
<footer>
<slot name="rodape">Footer padrão</slot>
</footer>
</div>
</template>
Slots com Escopo (Scoped Slots)
Permite que o componente filho exponha dados para o pai renderizar:
Crie src/components/ListaDados.vue:
<template>
<div class="lista-dados">
<slot name="item" v-bind:registro="registroAtual"></slot>
</div>
</template>
<script>
export default {
name: 'ListaDados',
data() {
return {
registroAtual: {
nome: "Maria Silva",
idade: 28,
profissao: "Desenvolvedora"
}
}
}
}
</script>
Uso no pai:
<ListaDados>
<template v-slot:item="escopo">
<p>{{ escopo.registro.nome }} - {{ escopo.registro.profissao }}</p>
</template>
</ListaDados>
Cache de Componentes com Keep-Alive
O <keep-alive> preserva o estado dos componentes quando alternamos entre eles.
Exemplo com Alternância de Componentes
Crie src/components/PerfilUsuario.vue:
<template>
<div class="perfil">
<h3>Perfil do Usuário</h3>
<input v-model="nome" placeholder="Digite seu nome" />
<p>Nome digitado: {{ nome }}</p>
</div>
</template>
<script>
export default {
name: 'PerfilUsuario',
data() {
return {
nome: ''
}
}
}
</script>
Crie src/components/Configuracoes.vue:
<template>
<div class="config">
<h3>Configurações</h3>
<p>Painel de configurações do sistema</p>
</div>
</template>
<script>
export default {
name: 'Configuracoes'
}
</script>
No src/App.vue:
<template>
<div id="app">
<button @click="alternar">Alternar View</button>
<!-- Sem cache -->
<div>
<component :is="viewAtual"></component>
</div>
<!-- Com cache -->
<keep-alive>
<component :is="viewAtual"></component>
</keep-alive>
</div>
</template>
<script>
import PerfilUsuario from "./components/PerfilUsuario"
import Configuracoes from "./components/Configuracoes"
export default {
name: 'App',
components: {
PerfilUsuario,
Configuracoes
},
data() {
return {
viewAtual: PerfilUsuario
}
},
methods: {
alternar() {
this.viewAtual = this.viewAtual === PerfilUsuario
? Configuracoes
: PerfilUsuario
}
}
}
</script>
Ciclo de Vida do Componente
Vue oferece hooks em diferentes fases do ciclo de vida:
- Criação:
beforeCreate()→created() - Montagem:
beforeMount()→mounted() - Atualização:
beforeUpdate()→updated() - Destruição:
beforeDestroy()→destroyed()
Exemplo Demonstrando os Hooks
Crie src/components/MonitorEstado.vue:
<template>
<div class="monitor">
<h3>Monitor de Estado</h3>
<p>Contador: {{ contador }}</p>
<button @click="incrementar">+1</button>
</div>
</template>
<script>
export default {
name: 'MonitorEstado',
data() {
return {
contador: 0
}
},
methods: {
incrementar() {
this.contador++
}
},
beforeCreate() {
console.log('[Hook] Antes de criar')
},
created() {
console.log('[Hook] Após criar')
},
beforeMount() {
console.log('[Hook] Antes de montar no DOM')
},
mounted() {
console.log('[Hook] Montado no DOM')
},
beforeUpdate() {
console.log('[Hook] Antes da atualização')
},
updated() {
console.log('[Hook] Após atualização')
},
beforeDestroy() {
console.log('[Hook] Antes de destruir')
},
destroyed() {
console.log('[Hook] Destruído')
}
}
</script>
Animações e Transições
Transições CSS
Utilize o componente <transition> para animar elementos:
<template>
<div class="animacao-demo">
<button @click="visivel = !visivel">Alternar</button>
<transition name="fade">
<p v-if="visivel">Conteúdo animado!</p>
</transition>
</div>
</template>
<script>
export default {
name: 'AnimacaoDemo',
data() {
return {
visivel: true
}
}
}
</script>
<style>
.fade-enter-active, .fade-leave-active {
transition: all 0.6s ease;
}
.fade-enter, .fade-leave-to {
opacity: 0;
transform: translateY(-10px);
}
</style>
Classes de Transição
- Entrada:
fade-enter(início),fade-enter-active(durante),fade-enter-to(fim) - Saída:
fade-leave(início),fade-leave-active(durante),fade-leave-to(fim)
Usando Bibliotecas de Animação (Animate.css)
Adicione o CSS no index.html:
<link href="https://cdn.jsdelivr.net/npm/animate.css@3.5.1" rel="stylesheet" type="text/css">
No template, especifique as classes de animação:
<transition
enter-active-class="animated slideInLeft"
leave-active-class="animated slideOutRight"
>
<p v-if="visivel">Com Animate.css!</p>
</transition>
Diretivas Customizadas
Diretiva Global
Declare em src/main.js:
import Vue from 'vue'
import App from './App'
Vue.directive('destaque', {
inserted(el, binding) {
el.style.backgroundColor = binding.value || '#ffffcc'
}
})
new Vue({
el: '#app',
render: h => h(App)
})
Diretiva Local
export default {
name: 'MeuComponente',
directives: {
maiusculo: {
inserted(el) {
el.style.textTransform = 'uppercase'
}
}
}
}
Uso no template:
<input v-destaque="'#ffe0b2'" />
<p v-maiusculo>este texto ficará em maiúsculas</p>
Filtros Customizados
Filtro Global
Declare em src/main.js:
Vue.filter('formatarMoeda', function(valor) {
return 'R$ ' + parseFloat(valor).toFixed(2)
})
Filtro Local
export default {
name: 'MeuComponente',
data() {
return {
titulo: 'vue componentes'
}
},
filters: {
capitalizar(valor) {
return valor.charAt(0).toUpperCase() + valor.slice(1)
}
}
}
Uso no template:
<p>{{ 1500 | formatarMoeda }}</p>
<p>{{ titulo | capitalizar }}</p>