Componentes 🧩
Esta página acompanha os slides, na mesma ordem. São quatro partes: criar um projeto, entender o arquivo .vue, enviar dados ao filho e receber avisos do filho. A matéria vem primeiro 📖. Os exercícios práticos 🛠️ ficam juntos no final da página.
A aula de Vue.js usou uma página HTML e a CDN. Aqui o mesmo Vue passa a viver em arquivos .vue, do jeito que um projeto real é escrito.
Parte 1: Criando um projeto Vue 🚀
Por que sair da página solta 📄
Um arquivo .vue reúne, num lugar só, a tela, a lógica e o CSS de um componente. O navegador lê HTML, CSS e JavaScript. Quem faz a tradução é o Vite ⚡, a ferramenta de construção que o Vue usa por padrão. Com ela, salvar o arquivo atualiza a página aberta, e o endereço da aplicação usa http, como o de um site de verdade.
O que o computador precisa 🧰
Três ferramentas, e a aula pode começar 🎉.
- 💻 VS Code, o editor da disciplina.
- 🎨 A extensão Vue - Official. No VS Code, abra o painel de extensões, procure esse nome e instale a que tem mais downloads. Ela colore o arquivo
.vuee completa o código enquanto você digita. Materiais mais antigos chamam essa extensão de Volar. É a mesma família. - 🟢 Node.js, o mesmo runtime da aula de Node. A documentação atual do Vue aceita a linha 22 a partir da versão 22.18, e também a versão 24.12 ou posterior. As versões da linha 23, e as da linha 24 anteriores a 24.12, ficam de fora dessa faixa.
No terminal:
node -v
O número que aparecer é a versão instalada. Se o comando não for encontrado, ou se a versão estiver fora da faixa, instale uma versão atual em nodejs.org e abra um terminal novo.
Criar o projeto ✨
O comando oficial pergunta o nome do projeto e monta a pasta. Na primeira vez ele baixa a ferramenta, então o computador precisa de internet.
- Crie uma pasta para reunir os projetos da disciplina, por exemplo
projetos, e abra essa pasta no VS Code. - Abra o terminal pelo menu Terminal, Novo Terminal.
- Confira se o terminal está na pasta certa. No macOS e no Linux,
ls. No Windows,dir. - Rode
npm create vue@latest. - Quando pedir o nome, escreva
ola-vue. - O assistente oferece ferramentas extras: TypeScript, JSX, Vue Router, Pinia, testes, ESLint, Prettier e Vue DevTools. Escolha Não em todas. Enter confirma a opção marcada. O projeto fica pequeno e fácil de ler.
- No fim, o terminal sugere os próximos comandos. Rode, nesta ordem:
cd ola-vue
npm install
npm run dev
npm install baixa as bibliotecas do projeto. npm run dev liga o servidor de desenvolvimento. O terminal imprime um endereço local, em geral http://localhost:5173. Se o navegador não abrir sozinho, copie esse endereço. A página que aparece é o exemplo que o próprio Vue gerou 🎉.
Deixe esse terminal aberto enquanto trabalha. Cada salvamento no editor atualiza a tela. Para encerrar o servidor, use Ctrl+C nesse terminal.
Parte 2: Arquivos Vue e componentes 🗂️
O componente 🧩
Um componente é um trecho da interface com uma função clara. Pode ser uma saudação, um cartão de livro ou um formulário. Sozinho, ele é pequeno. Junto com outros, forma a página.
O mesmo componente pode aparecer várias vezes, e cada uso pode receber dados diferentes. No Vue, ele fica gravado num arquivo .vue.
O projeto que você acabou de criar já mostra um componente na tela: o arquivo src/App.vue. Os componentes novos entram nele. Os outros arquivos gerados podem permanecer na pasta. A tela mostra só o que App.vue usa.
O arquivo .vue 📁
Esse arquivo também é chamado de SFC, sigla em inglês para componente de arquivo único. Três blocos ficam juntos:
<template>descreve o HTML do componente. Vale o que você já usou na aula de Vue:{{ }},v-if,v-for,v-modele@click.<script setup>guarda os dados e as funções. O que for declarado ali fica disponível no template. Essa escrita é a forma atual do Vue 3, a Composition API. As variáveis e as funções ficam declaradas direto nesse script, no lugar dedata()emethodsda aula com CDN.<style scoped>guarda o CSS do componente. Comscoped, a regra vale para o HTML deste arquivo. Outro componente pode ter um<p>de outra cor.
A ordem dos blocos é livre. Nos exemplos desta aula o template vem primeiro, para a tela aparecer antes da lógica. O projeto gerado às vezes traz o script no topo. Os dois jeitos funcionam.
Um primeiro componente, src/components/Saudacao.vue:
<template>
<p>Olá, {{ curso }}!</p>
</template>
<script setup>
const curso = 'CPW2'
</script>
<style scoped>
p {
color: teal;
}
</style>
Leia de cima para baixo. O template escreve um parágrafo e pede o valor de curso. O script declara essa constante. O style pinta o parágrafo de verde azulado, e só o deste componente.
curso é texto fixo. Uma constante dá conta disso. Quando o valor muda e a tela precisa acompanhar, entra o ref, na parte 3.
Usar o componente 🔗
App.vue importa o arquivo e usa o nome dele como tag. O import já registra o componente. A inicial maiúscula ajuda o olho: <Saudacao /> é um componente nosso, <p> é HTML.
Substitua o conteúdo de src/App.vue por:
<template>
<Saudacao />
</template>
<script setup>
import Saudacao from './components/Saudacao.vue'
</script>
O nome da tag, o nome do import e o nome do arquivo se encontram: Saudacao, sem acento, em Saudacao.vue. Salve os dois arquivos. A página que já está aberta passa a mostrar “Olá, CPW2!” 👋.
Parte 3: Propriedades 🏷️
Props ⬇️
Até aqui a saudação traz o curso escrito dentro do próprio componente. O mais útil é o componente receber o dado de quem o usa. Esse dado se chama prop, de property, propriedade.
A prop vai do componente pai para o componente filho. O pai escolhe o valor. O filho declara o nome da prop e mostra esse valor na tela.
defineProps declara a prop que o componente aceita:
<template>
<p>Olá, {{ nome }}!</p>
</template>
<script setup>
defineProps({
nome: {
type: String,
required: true
}
})
</script>
type: String diz que nome é texto. required: true pede que o pai envie esse texto sempre. No template, nome aparece solto, como a constante curso aparecia antes.
No pai, o texto fixo vai entre aspas, sem dois pontos:
<template>
<Saudacao nome="Ana" />
</template>
<script setup>
import Saudacao from './components/Saudacao.vue'
</script>
A tela mostra “Olá, Ana!”.
Texto fixo e v-bind 🔗
Quando o nome mora numa variável, a tag usa v-bind. O atalho é os dois pontos, o mesmo : da aula de Vue:
<Saudacao :nome="aluno" />
nome="Ana" entrega o texto Ana. :nome="aluno" entrega o valor da variável aluno. nome="aluno", com aspas e sem os dois pontos, entrega a palavra aluno, letra por letra.
ref 🔄
No projeto, o dado que muda fica numa ref. Ela faz o papel de data() da aula com CDN: o Vue observa o valor e atualiza a tela quando ele troca.
<template>
<Saudacao :nome="aluno" />
</template>
<script setup>
import { ref } from 'vue'
import Saudacao from './components/Saudacao.vue'
const aluno = ref('Ana')
</script>
ref vem do pacote vue. No script, o texto está em aluno.value. No template, escreva aluno. O Vue lê o valor por você. Troque 'Ana' por outro nome, salve, e a saudação acompanha.
O mesmo componente, dois usos 👥
Duas tags, dois valores, um arquivo só:
<template>
<Saudacao nome="Ana" />
<Saudacao nome="Bruno" />
</template>
A tela mostra “Olá, Ana!” e “Olá, Bruno!”, as duas a partir do mesmo arquivo.
Parte 4: Emissão de eventos 📤
O evento ⬆️
A prop desce: o pai envia o valor, o filho mostra. O clique, porém, acontece dentro do filho. Quem atualiza um dado do pai é o próprio pai. O filho avisa que algo ocorreu, e o pai decide o que fazer com o aviso.
O aviso é um evento. Nesta aula o evento se chama cumprimentou.
Disparar com emit 📤
defineEmits lista os eventos que o componente pode disparar. A função emit dispara o evento:
<script setup>
const emit = defineEmits(['cumprimentou'])
function avisar() {
emit('cumprimentou')
}
</script>
<template>
<button @click="avisar">Dizer olá</button>
</template>
@click continua sendo o clique do mouse, como na aula de Vue. emit é o passo seguinte: contar ao pai que esse clique importa.
O pai escuta o evento 👂
Na tag, @cumprimentou escuta o evento. É o mesmo atalho de v-on que você já usa em @click, agora com um nome criado por você.
<template>
<Saudacao nome="Ana" @cumprimentou="registrar" />
<p>{{ recado }}</p>
</template>
<script setup>
import { ref } from 'vue'
import Saudacao from './components/Saudacao.vue'
const recado = ref('Ninguém cumprimentou ainda.')
function registrar() {
recado.value = 'A mensagem chegou.'
}
</script>
O parágrafo do recado mora no pai. A função registrar também. O botão mora no filho. O clique percorre esse caminho: botão, avisar, emit, @cumprimentou, registrar, recado.
O evento leva o nome 💬
O emit aceita um segundo argumento, o conteúdo do recado. Para ler a prop dentro do script, guarde o retorno de defineProps:
<script setup>
const props = defineProps({
nome: {
type: String,
required: true
}
})
const emit = defineEmits(['cumprimentou'])
function avisar() {
emit('cumprimentou', props.nome)
}
</script>
No template, o nome segue solto: {{ nome }}. No script, ele fica em props.nome.
O pai recebe esse texto no parâmetro da função:
function registrar(nome) {
recado.value = nome + ' cumprimentou.'
}
Com <Saudacao nome="Ana" @cumprimentou="registrar" />, o clique troca o parágrafo para “Ana cumprimentou.”.
Nomes de uma palavra bastam por enquanto. Se um evento precisar de duas, a tag e o emit usam kebab-case, com o mesmo texto nos dois lados: emit('livro-salvo') e @livro-salvo.
A aula de Vue com CDN escrevia a aplicação como um objeto, com data() e methods. Um arquivo .vue aceita esse formato: o objeto começa com export default e o <script> fica sem setup. O exemplo completo fecha esta parte, depois dos dois arquivos com <script setup>.
Os dois arquivos juntos 🤝
src/components/Saudacao.vue:
<template>
<div>
<p>Olá, {{ nome }}!</p>
<button @click="avisar">Dizer olá</button>
</div>
</template>
<script setup>
const props = defineProps({
nome: {
type: String,
required: true
}
})
const emit = defineEmits(['cumprimentou'])
function avisar() {
emit('cumprimentou', props.nome)
}
</script>
<style scoped>
p {
color: teal;
}
</style>
src/App.vue:
<template>
<Saudacao nome="Ana" @cumprimentou="registrar" />
<Saudacao nome="Bruno" @cumprimentou="registrar" />
<p>{{ recado }}</p>
</template>
<script setup>
import { ref } from 'vue'
import Saudacao from './components/Saudacao.vue'
const recado = ref('Ninguém cumprimentou ainda.')
function registrar(nome) {
recado.value = nome + ' cumprimentou.'
}
</script>
Ana e Bruno são o mesmo componente, com props diferentes. O clique de cada botão chama a mesma função do pai e envia o próprio nome.
O caminho dos dados 🗺️:
flowchart TD
pai[Pai] -->|props| filho[Filho]
filho -->|evento| pai
Props descem ⬇️. Eventos sobem ⬆️. Cada dado tem um dono, e a tela fica previsível 🎯.
A mesma tela com export default
src/components/Saudacao.vue:
<template>
<div>
<p>Olá, {{ nome }}!</p>
<button @click="avisar">Dizer olá</button>
</div>
</template>
<script>
export default {
props: {
nome: {
type: String,
required: true
}
},
emits: ['cumprimentou'],
methods: {
avisar() {
this.$emit('cumprimentou', this.nome)
}
}
}
</script>
<style scoped>
p {
color: teal;
}
</style>
src/App.vue:
<template>
<Saudacao nome="Ana" @cumprimentou="registrar" />
<Saudacao nome="Bruno" @cumprimentou="registrar" />
<p>{{ recado }}</p>
</template>
<script>
import Saudacao from './components/Saudacao.vue'
export default {
components: {
Saudacao
},
data() {
return {
recado: 'Ninguém cumprimentou ainda.'
}
},
methods: {
registrar(nome) {
this.recado = nome + ' cumprimentou.'
}
}
}
</script>
A tela faz o mesmo. O clique da Ana mostra “Ana cumprimentou.” e o do Bruno mostra “Bruno cumprimentou.”.
No filho, a prop fica dentro de props. No template, nome continua solto. No método, o valor é this.nome. O evento entra na lista emits e sai por this.$emit.
No pai, recado nasce em data() e a função fica em methods. A atribuição é this.recado = ..., sem .value. O import sozinho não cria a tag. O pai registra o filho em components.
Exercícios de fixação práticos 🛠️
A matéria fica acima. Os doze exercícios seguem a ordem das quatro partes 💪. Salve cada mudança e olhe o navegador.
Parte 1: criar o projeto 🚀
Exercício 1. O Node responde 🟢
No terminal, rode node -v.
👀 Resultado esperado: uma linha com a versão, dentro da faixa 22.18 ou posterior na linha 22, ou 24.12 ou posterior.
💡 Dica: se a mensagem disser que o comando não existe, instale o Node.js e abra outro terminal. Um terminal antigo continua sem enxergar o programa novo.
Exercício 2. A pasta do projeto 📁
Crie o projeto ola-vue com npm create vue@latest, respondendo Não às perguntas extras. Se você já criou essa pasta ao ler a parte 1, use a mesma.
👀 Resultado esperado: existe a pasta ola-vue e, dentro dela, o arquivo package.json.
💡 Dica: rode o comando na pasta que guarda vários projetos. Ele mesmo cria ola-vue. Rodar de novo dentro de ola-vue criaria um projeto dentro do outro.
Exercício 3. A página no navegador 🌐
Dentro de ola-vue, rode npm install e depois npm run dev.
👀 Resultado esperado: o terminal mostra um endereço local e o navegador exibe a página inicial do Vue.
💡 Dica: use o endereço impresso no terminal, mesmo que a porta não seja
- Mantenha esse terminal aberto para os exercícios seguintes.
Parte 2: o componente 🧩
O servidor da parte 1 continua ligado. Cada exercício é uma mudança pequena. Salve e olhe o navegador.
Exercício 4. Um título só 📰
Substitua todo o conteúdo de src/App.vue por um template com <h1>Meu primeiro componente</h1> e um <script setup> vazio.
👀 Resultado esperado: a página inicial do Vue some e fica só esse título.
💡 Dica: apague o conteúdo anterior do arquivo. Um trecho antigo de import impede a página de abrir.
Exercício 5. O título vira componente 🧩
Crie src/components/Saudacao.vue com o mesmo <h1> no template. Em App.vue, importe Saudacao e troque o <h1> pela tag <Saudacao />.
👀 Resultado esperado: o título continua na tela, agora vindo do arquivo novo.
💡 Dica: o caminho do import, a partir de src/App.vue, é ./components/Saudacao.vue. O nome do arquivo vai sem acento.
Exercício 6. O CSS do componente 🎨
No Saudacao.vue, acrescente um bloco <style scoped> que pinte o h1 de teal.
👀 Resultado esperado: o título fica verde azulado.
💡 Dica: a regra CSS fica no arquivo do componente, junto do template dele.
Parte 3: props 🏷️
Exercício 7. A prop nome 🏷️
Em Saudacao.vue, troque o título fixo por Olá, {{ nome }}!. Declare a prop nome como String obrigatória. Em App.vue, use <Saudacao nome="Ana" />.
👀 Resultado esperado: a tela mostra “Olá, Ana!” em verde azulado.
💡 Dica: nome no template é a prop. As aspas em nome="Ana" mandam o texto fixo.
Exercício 8. O nome sai de uma ref 🔄
Crie const aluno = ref('Ana') no script de App.vue e passe :nome="aluno". Importe ref de vue.
👀 Resultado esperado: a saudação continua “Olá, Ana!”. Mude o texto dentro do ref, salve, e o título muda junto.
💡 Dica: os dois pontos ligam a prop à variável. No script, o valor fica em aluno.value. Na tag, use aluno.
Exercício 9. Duas pessoas 👥
Mostre duas tags Saudacao, uma com nome="Ana" e outra com nome="Bruno".
👀 Resultado esperado: dois títulos, “Olá, Ana!” e “Olá, Bruno!”.
💡 Dica: as duas tags usam o mesmo arquivo. Cada uma recebe o próprio nome.
Parte 4: eventos 📤
Exercício 10. O filho só avisa 📣
Volte a uma única Saudacao, com nome="Ana". No filho, coloque um botão “Dizer olá” que emite cumprimentou, ainda sem enviar o nome. No pai, um parágrafo começa com “Esperando um clique.” e a função do evento troca esse texto para “A mensagem chegou.”.
👀 Resultado esperado: o clique muda o parágrafo que está em App.vue.
💡 Dica: a função que escreve em recado.value fica no pai. O filho só chama emit.
Exercício 11. O nome no evento 💬
Passe props.nome como segundo argumento de emit. No pai, o parâmetro da função monta a frase com esse nome.
👀 Resultado esperado: o clique mostra “Ana cumprimentou.”.
💡 Dica: guarde defineProps em const props para usar props.nome no script. No template, nome continua solto.
Exercício 12. Duas saudações, um pai 👥
Inclua de novo a segunda saudação, com nome="Bruno", ouvindo o mesmo registrar.
👀 Resultado esperado: o botão da Ana mostra “Ana cumprimentou.” e o botão do Bruno mostra “Bruno cumprimentou.”.
💡 Dica: as duas tags apontam para a mesma função. O nome que chega é o do componente que disparou o evento.
Exercício de fixação teórico 🧠
Referências 📚
-
Criar um projeto Vue, documentação oficial
-
Componentes, documentação oficial
-
Props e eventos de componente, documentação oficial
-
Scaling up Vue, roteiro de instalação usado como ponto de partida da parte 1

CC BY 4.0 DEED