4e étageV VuePièce 4/7Intermédiaire

Props, événements et v-model sur un composant

Faire communiquer un composant parent et son enfant avec defineProps, defineEmits et defineModel.

Ce que tu vas apprendre

  • Passer des données d'un parent à son enfant avec les props.
  • Remonter une information de l'enfant vers le parent avec un événement.
  • Utiliser v-model sur ton propre composant avec defineModel().

Les props : du parent vers l'enfant

Une prop est une donnée qu'un composant reçoit de son parent. L'enfant la déclare avec defineProps().

src/components/CarteRecette.vue
<script setup lang="ts">
defineProps<{
  titre: string
  minutes?: number
}>()
</script>

<template>
  <article>
    <h3>{{ titre }}</h3>
    <p v-if="minutes">{{ minutes }} min</p>
  </article>
</template>

Le parent les passe comme des attributs :

src/App.vue
<template>
  <CarteRecette titre="Crêpes" :minutes="20" />
</template>

Un texte fixe s'écrit tel quel (titre="Crêpes"). Pour une valeur JavaScript, on utilise v-bind : :minutes="20". Le ? dans le type signifie que la prop est facultative.

Dans le template, les props s'utilisent directement. Dans le script, defineProps() renvoie un objet si tu le stockes : const props = defineProps<...>().

Une valeur par défaut

Avec la déstructuration, une prop facultative reçoit sa valeur par défaut comme n'importe quelle variable :

const { titre, minutes = 30 } = defineProps<{
  titre: string
  minutes?: number
}>()

À partir de Vue 3.5, ces variables restent réactives. Avant la 3.5, elles étaient de simples constantes qui ne se mettaient jamais à jour.

Le flux est à sens unique

Les données descendent du parent vers l'enfant, jamais l'inverse. Une prop est en lecture seule : props.titre = 'autre' provoque un avertissement.

Ne modifie jamais une prop dans l'enfant. Si l'enfant a besoin de changer la valeur, il prévient le parent avec un événement, et c'est le parent qui la modifie.

Les événements : de l'enfant vers le parent

Un composant déclare ses événements avec defineEmits() et les déclenche avec emit().

src/components/BoutonFavori.vue
<script setup lang="ts">
const emit = defineEmits<{
  (e: 'choisir', id: number): void
}>()
</script>

<template>
  <button @click="emit('choisir', 42)">Ajouter aux favoris</button>
</template>

Le parent écoute avec @ :

<script setup lang="ts">
function noter(id: number) {
  console.log('Favori :', id)
}
</script>

<template>
  <BoutonFavori @choisir="noter" />
</template>

Les arguments de emit après le nom arrivent dans le même ordre dans la fonction du parent.

Le v-model sur un composant

Que se passe-t-il si tu écris <ChampTitre v-model="titre" /> ? Il faut une prop pour recevoir la valeur et un événement pour la renvoyer. Depuis Vue 3.4, la macro defineModel() fait les deux d'un coup.

src/components/ChampTitre.vue
<script setup>
const titre = defineModel()
</script>

<template>
  <input v-model="titre" />
</template>

titre se comporte comme un ref. Quand l'enfant le modifie, le parent est prévenu et sa propre variable change.

src/App.vue
<script setup>
import { ref } from 'vue'
import ChampTitre from './components/ChampTitre.vue'

const titre = ref('Tarte aux pommes')
</script>

<template>
  <ChampTitre v-model="titre" />
  <p>{{ titre }}</p>
</template>

Plusieurs modèles et options

Tu peux donner un nom au modèle, et ajouter des options comme required ou default :

const titre = defineModel('titre', { required: true })
const note = defineModel('note', { default: 0 })

Le parent écrit alors v-model:titre="..." et v-model:note="...". Un composant peut ainsi exposer plusieurs valeurs liées.

Quel outil choisir ?

BesoinOutil
Donner une donnée à l'enfantUne prop
Prévenir le parent d'une actionUn événement
Un champ que l'enfant peut modifierdefineModel()
Pose-toi la question : « qui possède cette donnée ? ». Le composant qui la possède la déclare, les autres la reçoivent ou demandent un changement.

À retenir

  • defineProps() déclare les données reçues du parent ; elles sont en lecture seule.
  • Les données descendent par les props, les informations remontent par les événements.
  • defineEmits() déclare les événements, emit('nom', valeur) les déclenche, @nom les écoute.
  • defineModel() crée une prop et son événement pour utiliser v-model sur un composant.
  • Avec defineModel('nom'), le parent écrit v-model:nom.

Pour aller plus loin

Sources officielles

Ensuite

Projet
Lignes de code
Niveau
4e étage
Appartement
Vue
Pièce
4/7
Plan n°
vue/04.props-et-evenements
Lignes de code · fiches rédigées à partir des documentations officielles, sources citées en bas de chaque page.