Props e Comunicação entre Componentes

Programação Web 2
Me. Danilo Bueno

O que vimos na última aula

  • Criar projetos com Vite + Vue 3
  • Componentizar a interface em arquivos .vue
  • Renderizar listas com v-for
  • Buscar dados com fetch e ref

O problema

<!-- AlbumCard.vue --><template>  <div class="card">    <h2>Kind of Blue</h2>    <!-- fixo -->    <p>Miles Davis</p>    <!-- fixo -->  </div></template>

Para exibir 10 álbuns diferentes, precisaria de 10 componentes diferentes.

Isso destrói o propósito da componentização.

A solução: Props

Props são dados que o pai passa para o filho.

O filho declara o que espera receber.
O pai fornece os valores ao usar o componente.

App.vue (pai)
   │
   │  :titulo="album.titulo"
   │  :artista="album.artista"
   ▼
AlbumCard.vue (filho)
   defineProps(['titulo', 'artista'])

defineProps — filho declara o que espera

<!-- AlbumCard.vue --><template>  <div class="card">    <h2>{{ titulo }}</h2>    <p>{{ artista }}</p>    <span>⭐ {{ nota }}</span>  </div></template><script setup>defineProps(["titulo", "artista", "nota"]);</script>

Passando props no pai

<!-- App.vue --><template>  <AlbumCard titulo="Kind of Blue" artista="Miles Davis" :nota="9.8" />  <AlbumCard titulo="Abbey Road" artista="The Beatles" :nota="9.3" /></template>

Strings: sem :. Números, booleanos e variáveis: com :.

v-for + props — listagem dinâmica

<template>  <AlbumCard    v-for="album in albuns"    :key="album.id"    :titulo="album.titulo"    :artista="album.artista"    :nota="album.nota"  /></template><script setup>import AlbumCard from "./components/AlbumCard.vue";const albuns = [  { id: 1, titulo: "Kind of Blue", artista: "Miles Davis", nota: 9.8 },  { id: 2, titulo: "Abbey Road", artista: "The Beatles", nota: 9.3 },];</script>

Atalho: v-bind="objeto"

Quando o objeto tem as mesmas chaves que as props do filho:

<!-- Sem atalho --><AlbumCard :titulo="album.titulo" :artista="album.artista" :nota="album.nota" /><!-- Com atalho — equivalente --><AlbumCard v-bind="album" />

v-bind="album" expande cada chave do objeto como uma prop separada.

Fluxo unidirecional

Dados descem do pai para o filho via props.

O filho não pode modificar props diretamente.

// ❌ Vue exibe warning — props são somente leituraprops.titulo = "Outro título";

Se o filho precisa comunicar algo ao pai, usa emits.

Emits — o filho fala com o pai

O filho emite um evento. O pai escuta e reage.

AlbumCard.vue (filho)
   emit('adicionarFila', titulo)
   │
   │  @adicionarFila="receberAlbum"
   ▼
App.vue (pai)
   function receberAlbum(titulo) { ... }

defineEmits — filho declara e dispara

<!-- AlbumCard.vue --><template>  <div class="card">    <h2>{{ titulo }}</h2>    <button @click="emit('adicionarFila', titulo)">+ Adicionar à fila</button>  </div></template><script setup>defineProps(["titulo", "artista", "nota"]);const emit = defineEmits(["adicionarFila"]);</script>

Escutando o evento no pai

<!-- App.vue --><template>  <p v-if="filaAtual">Na fila: {{ filaAtual }}</p>  <AlbumCard    v-for="album in albuns"    :key="album.id"    v-bind="album"    @adicionarFila="receberAlbum"  /></template><script setup>import { ref } from "vue";const filaAtual = ref("");function receberAlbum(titulo) {  filaAtual.value = titulo;}</script>

Regra de ouro

Direção Mecanismo
Pai → Filho Props (:prop="valor")
Filho → Pai Emits (emit('evento', dado))

Props descem. Eventos sobem.

Nunca ao contrário.

Referência rápida

Recurso Sintaxe
Declarar props defineProps(['titulo', 'nota'])
Acessar props no script const props = defineProps([...])
Passar prop do pai :titulo="album.titulo"
Passar objeto inteiro v-bind="album"
Declarar emits const emit = defineEmits(['evento'])
Disparar evento emit('evento', dado)
Escutar no pai @evento="handler"

Obrigado!

Me. Danilo Bueno
danilocbueno.github.io