Cours Vue.js · Module 7: Props et événements

Les bases des props

6 min de lecture
Dans cette leçon6

Bienvenue à la station de communication du NOVA LAB ! Ici, les systèmes orbitaux transmettent les paramètres de mission aux modules martiens - exactement comme les composants parents transmettent des props à leurs enfants. Chaque signal compte, car selon la position des planètes, un message de la Terre met d'environ 3 à plus de 20 minutes pour atteindre Mars.

Imagine un panneau qui affiche l'état d'un module de la station : son nom et sa température. Le générateur d'oxygène, le filtre à eau et la serre y apparaissent de façon identique, seules les données changent. Si le composant avait ces données écrites en dur, tu devrais le copier trois fois et surveiller trois fichiers à chaque correction. Il te faut un seul composant dont tu fournis les paramètres de l'extérieur - et c'est exactement à cela que servent les props.

Ce que tu vas apprendre

À la station de communication, tu vas maîtriser tous les canaux de liaison entre composants :

  • transmettre des données du parent vers l'enfant avec les props, puis les typer et les valider,
  • envoyer des signaux de retour de l'enfant vers le parent avec tes propres événements,
  • construire des composants qui prennent en charge v-model,
  • diffuser des données en profondeur dans l'arbre de composants avec provide et inject,
  • réunir ces canaux dans une architecture de communication cohérente.

Que sont les props ?

Les props (de l'anglais properties) sont un mécanisme qui permet de transmettre des données d'un composant parent vers un composant enfant. C'est un flux de données à sens unique - comme un signal radio de la Terre vers Mars. L'enfant déclare la liste de ses paramètres avec la macro defineProps, qui fonctionne dans <script setup> sans import, et le parent fournit les valeurs comme des attributs dans le template :

1<!-- Parent.vue -->
2<template>
3  <MarsModule name="Oxygen Generator" :temperature="-63" />
4</template>
5
6<!-- MarsModule.vue -->
7<script setup>
8const props = defineProps(['name', 'temperature'])
9</script>
10
11<template>
12  <div>{{ name }}: {{ temperature }}°C</div>
13</template>

Remarque les deux-points devant temperature. C'est le raccourci de v-bind, grâce auquel -63 arrive chez l'enfant sous forme de nombre et non de texte ; l'attribut name sans deux-points transmet une simple chaîne. Dans le template de l'enfant, les props sont accessibles directement par leur nom, et dans le script via l'objet renvoyé par defineProps, c'est-à-dire props.name. Le parent n'a pas modifié une seule ligne de l'enfant - il a seulement fourni des valeurs.

Props typées

Un tableau de noms indique quels signaux le module accepte, mais pas de quel type ils sont. La syntaxe objet permet d'associer un type à chaque nom, c'est-à-dire un constructeur comme String, Number, Boolean, Array ou Object - comme une spécification de protocole de transmission :

1<script setup>
2const props = defineProps({
3  name: String,
4  temperature: Number,
5  critical: Boolean,
6  modules: Array,
7  config: Object
8})
9</script>

Un type ne convertit rien. Si le parent passe du texte là où tu attends un nombre, Vue affiche un avertissement dans la console en mode développement, mais la valeur arrive quand même jusqu'au composant - les types sont un système d'alerte précoce, pas une barrière. Déclare les noms composés en camelCase, par exemple moduleName ; dans le template du parent, tu peux les écrire module-name, car Vue fait correspondre les deux formes.

Transmettre un objet avec v-bind

Parfois, tu as déjà rassemblé les données du module dans un seul objet. Au lieu de recopier chaque champ séparément, transmets l'objet entier avec v-bind sans argument - chacune de ses propriétés arrive chez l'enfant comme une prop distincte :

1<template>
2  <!-- Individuellement -->
3  <MarsModule :name="data.name" :status="data.status" />
4
5  <!-- Toutes d'un coup -->
6  <MarsModule v-bind="data" />
7</template>
8
9<script setup>
10import { reactive } from 'vue'
11
12const data = reactive({
13  name: 'Oxygen Generator',
14  status: 'operational',
15  temperature: -63
16})
17</script>

La deuxième ligne transmet aussi temperature, que la première ne mentionne même pas. Grâce à reactive, chaque modification de l'objet data parvient immédiatement à l'enfant. Les champs que l'enfant n'a pas déclarés comme props ne se perdent pas - ils deviennent des attributs transmis plus loin, que nous examinerons dans la leçon sur les attributs fallthrough.

Props booléennes

Les props de type Boolean imitent les attributs HTML comme disabled, c'est pourquoi elles ont leur propre règle : la présence de l'attribut signifie true, et son absence signifie false :

1<template>
2  <SystemModule critical />          <!-- critical = true -->
3  <SystemModule :critical="true" />  <!-- true explicite -->
4  <SystemModule :critical="false" /> <!-- false explicite -->
5  <SystemModule />                   <!-- critical = false (par défaut) -->
6</template>

Cette règle ne concerne que les props déclarées comme Boolean. Une prop absente de n'importe quel autre type vaut undefined, et non false, donc le template de l'enfant doit s'y préparer.

Flux de données à sens unique

La règle la plus importante de cette station : les données ne circulent que vers le bas. Le parent est propriétaire des données, et l'enfant les reçoit en lecture. Quand le parent modifie une valeur, Vue rafraîchit l'enfant tout seul, mais l'enfant ne peut pas écraser une prop :

1<script setup>
2const props = defineProps(['title'])
3
4// FAUX - ne modifie pas les props !
5// props.title = 'Nouveau titre'
6
7// Crée une copie locale
8import { ref } from 'vue'
9const localTitle = ref(props.title)
10</script>

Une tentative d'affectation se termine par un avertissement dans la console indiquant que la cible est en lecture seule, et la valeur reste inchangée. Une copie locale dans un ref convient bien quand la prop n'est qu'une valeur de départ. Souviens-toi cependant de ce que la copie ne fait pas : si le parent modifie title plus tard, localTitle ne suivra pas. Décris les valeurs calculées à partir d'une prop avec computed, et quand l'enfant veut vraiment modifier les données du parent, qu'il lui envoie un signal - c'est le sujet de la leçon sur les événements personnalisés.

Mon conseil : dès ton premier composant, déclare les props avec la syntaxe objet et des types. Un tableau de noms suffit pour un brouillon, mais les types détecteront l'erreur du parent avant que l'équipage sur Mars ne la remarque. Dans la prochaine leçon, tu leur ajouteras une validation : champs requis, valeurs par défaut et conditions personnalisées. Dans l'éditeur ci-dessous, le même module apparaît trois fois - avec les props passées une par une, via v-bind et avec l'indicateur critical.

Retiens : les props sont un signal du centre de contrôle - le module le reçoit et l'affiche, mais ne l'écrase jamais.

Code de cette leçon : App.vue
1<script setup>
2import { reactive } from 'vue'
3import MarsModule from './MarsModule.vue'
4
5const moduleData = reactive({
6  name: 'Oxygen Generator Alpha',
7  status: 'operational',
8  temperature: -63
9})
10</script>
11
12<template>
13  <div class="nova-lab">
14    <h1>NOVA LAB - Props Demo</h1>
15
16    <!-- Transmettre les props individuellement -->
17    <MarsModule
18      :name="moduleData.name"
19      :status="moduleData.status"
20      :temperature="moduleData.temperature"
21    />
22
23    <!-- Transmettre toutes les props d'un coup -->
24    <MarsModule v-bind="moduleData" />
25
26    <!-- Prop booléenne - présence = true -->
27    <MarsModule v-bind="moduleData" critical />
28  </div>
29</template>
30
31<style scoped>
32.nova-lab {
33  background: #0a0e27;
34  color: #00ff88;
35  min-height: 100vh;
36  padding: 20px;
37  font-family: monospace;
38}
39h1 { color: #00ff88; margin-bottom: 20px; }
40</style>

Tu as repéré une erreur dans cette leçon ?

Teste tes connaissances

Réponds aux questions de cette leçon. Choisis une réponse pour voir tout de suite si elle est correcte.

  1. 1. Que sont les props dans Vue.js ?

  2. 2. Comment déclarer les props dans <script setup> ?

Voici 2 des 3 questions de cette leçon. Réponds aux autres dans le jeu.

Tâches pratiques dans le jeu

  • Éditeur de code

    Crée un composant MarsSignal qui accepte les props message et delay.

  • Clique dans l'ordre

    Ordonne la définition de props avec type :

  • Éditeur de code

    Crée un composant SystemModule avec des props typées : name (String), temperature (Number), critical (Boolean).

  • Tri vertical

    Ordonne le flux de données des props, du composant parent jusqu'à l'affichage :