Étape 2 — Extraire le composant PokemonCard
Objectifs
- Comprendre pourquoi découper en composants (réutilisabilité, lisibilité)
- Créer un composant avec
defineProps() - Passer des données du parent à l'enfant via les props
Contexte
Actuellement, tout le code de la carte Pokémon est dans index.vue. Si on veut réutiliser cette carte ailleurs (page favoris, page détail), il faut la dupliquer. La solution : extraire un composant réutilisable.
Avantages :
- Réutilisable : la même carte sur la page d'accueil, les favoris, etc.
- Lisible : chaque fichier a une seule responsabilité
- Maintenable : modifier la carte = modifier un seul fichier
Encart oral — Le build transforme votre code
Question fréquente : « Mon site est-il plus lent si je découpe en composants ? »
Non. Le navigateur ne reçoit jamais vos fichiers .vue. Quand vous lancez npm run build, l'outil Vite transforme tout votre code en quelques fichiers HTML, CSS et JS optimisés :
Que vous ayez 1 fichier ou 50 composants, le résultat du build est sensiblement le même : des fichiers optimisés, compressés et fusionnés. Le découpage en composants est un choix d'organisation pour le développeur, pas de performance pour le navigateur.
Testez vous-même : lancez npm run build et regardez le dossier dist/ — vous ne verrez aucun fichier .vue.
Tâches
1. Créer le composant PokemonCard.vue
Créez le fichier src/components/PokemonCard.vue.
Ce composant :
- Reçoit un objet
pokemonen prop (obligatoire) - Affiche la carte (image, nom, niveau) — le même code que dans
index.vue
defineProps({
pokemon: {
type: Object,
required: true,
},
})Encart oral — defineProps
defineProps() déclare les données que le parent envoie à l'enfant. C'est un courrier recommandé : le parent choisit quoi envoyer, l'enfant ne peut que lire (pas modifier).
type: Object— la prop doit être un objetrequired: true— le parent doit fournir cette prop
2. Utiliser PokemonCard dans index.vue
Dans src/pages/index.vue, remplacez le contenu de chaque <v-col> par le composant :
<pokemon-card :pokemon="pokemon" />Pourquoi l'import de getImageUrl disparaît ?
L'import import { getImageUrl } from '@/utils/imageUrl' n'est plus nécessaire dans index.vue — c'est maintenant PokemonCard.vue qui l'utilise. Quand on extrait du code dans un composant, les imports suivent le code.
Auto-import des composants
Grâce au plugin unplugin-vue-components, les composants dans src/components/ sont auto-importés. Pas besoin d'écrire import PokemonCard from '...'.
Mais pour la clarté pédagogique, on recommande d'ajouter l'import explicitement :
import PokemonCard from '@/components/PokemonCard.vue'3. Créer le composant AppFooter.vue
Créez src/components/AppFooter.vue avec un slot :
- Utilisez
<v-footer>comme conteneur - Ajoutez un
<slot>pour permettre au parent de personnaliser le contenu - Par défaut (si aucun contenu n'est fourni), affichez l'année + "Pokédex"
Encart oral — Slots
Un slot est un trou dans un composant que le parent peut remplir avec du contenu HTML. Si le parent ne fournit rien, le contenu par défaut (entre les balises <slot>) s'affiche.
4. Ajouter AppFooter dans App.vue
Remplacez le <v-footer> existant dans App.vue par <app-footer />.
Tests
- La grille de Pokémon s'affiche comme avant
- Le composant PokemonCard reçoit bien la prop (vérifier avec Vue DevTools)
- Le footer s'affiche avec l'année courante
- 0 erreurs dans la console
Solutions
src/components/PokemonCard.vue
<template>
<v-card hover>
<v-img
:src="getImageUrl(pokemon.img)"
:alt="pokemon.name"
height="200"
cover
/>
<v-card-title>{{ pokemon.name }}</v-card-title>
<v-card-subtitle>Niveau {{ pokemon.level }}</v-card-subtitle>
</v-card>
</template>
<script setup>
import { getImageUrl } from '@/utils/imageUrl'
defineProps({
pokemon: {
type: Object,
required: true,
},
})
</script>src/components/AppFooter.vue
<template>
<v-footer>
<div class="px-4 text-center w-100">
<slot>{{ new Date().getFullYear() }} - Pokédex</slot>
</div>
</v-footer>
</template>src/App.vue (modifications)
<template>
<v-app>
<app-header />
<v-main>
<router-view />
</v-main>
<app-footer />
</v-app>
</template>
<script setup>
import AppHeader from '@/components/AppHeader.vue'
import AppFooter from '@/components/AppFooter.vue'
</script>src/pages/index.vue (modifications)
<template>
<v-container>
<h1 class="text-h3 text-center my-6">Pokédex</h1>
<v-row>
<v-col
v-for="pokemon in pokemons"
:key="pokemon.id"
cols="12"
sm="6"
md="4"
lg="3"
>
<pokemon-card :pokemon="pokemon" />
</v-col>
</v-row>
</v-container>
</template>
<script setup>
import PokemonCard from '@/components/PokemonCard.vue'
const pokemons = ref([])
onMounted(async () => {
const response = await fetch('http://localhost:3535/pokemons')
pokemons.value = await response.json()
})
</script>Commit
git add -A
git commit -m "feat: composants PokemonCard et AppFooter"