Adloun

Composants

Cours · Vue : construire une application web (école d'ingénieur), chapitre 5 · école d'ingénieur, informatique · le début du chapitre, la suite sur Adloun

Au chapitre précédent, la page du catalogue de la Librairie du Campus est devenue une application Vue : une recherche, une liste de livres, un formulaire. Mais tout tient dans un seul gabarit et une seule fonction setup. La carte d'un livre, avec son image, son titre, son prix et son bouton, est écrite une fois dans une boucle v-for ; si la page d'accueil veut montrer les nouveautés avec les mêmes cartes, il faudra recopier ce balisage, et le corriger deux fois à chaque changement.

Vue répond à ce problème par les composants : des morceaux d'interface qui ont un nom, leur propre gabarit, leur propre état, et que l'on utilise comme de nouvelles balises HTML. Un composant est à ses instances ce qu'une classe Java est à ses objets : la carte d'un livre est définie une fois, et chaque livre affiché en est une instance, avec ses propres données. Ce chapitre montre comment définir un composant, comment lui transmettre des données (les props), comment il signale ce qui se passe (les événements émis), comment lui confier un morceau de contenu (les emplacements), et comment en afficher une liste sans les mélanger. Nous restons ici avec Vue chargé par un CDN, comme dans les chapitres précédents ; le chapitre suivant installera les outils qui rendent les composants plus confortables encore.

Objectifs du chapitre

  • Définir un composant, l'enregistrer et l'utiliser dans un gabarit.
  • Déclarer des props typées et validées, et comprendre le flux descendant des données : une prop ne se modifie pas.
  • Déclarer et émettre des événements, et les écouter dans le parent.
  • Confier un contenu à un composant avec un emplacement (<slot>).
  • Afficher une liste de composants avec une clé stable.
  • Éviter les pièges des gabarits écrits directement dans la page.

Les rendus et les messages de console de ce chapitre ont été produits dans Chromium, avec la version de développement de Vue 3.5.43 chargée depuis le CDN. Les avertissements de Vue ([Vue warn]) sont recopiés tels quels : la version de développement les écrit en anglais.

5.1 Un premier composant

Voici une page qui affiche trois livres avec un composant CarteLivre.


<!DOCTYPE html>
<html lang="fr">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Un premier composant</title>
  <link rel="stylesheet" href="../exemple.css">
  <script src="https://unpkg.com/vue@3.5.43/dist/vue.global.js"></script>
  <script src="app.js" defer></script>
</head>
<body>
  <div id="app">
    <h1>Nouveautés</h1>
    <carte-livre titre="Programmation Java" auteur="I. Alame" :prix="32"></carte-livre>
    <carte-livre titre="Analyse MPSI" auteur="J.-M. Monier" :prix="45"></carte-livre>
    <carte-livre titre="Le Petit Prince" auteur="A. de Saint-Exupéry" :prix="8.5"></carte-livre>
  </div>

  <template id="modele-carte-livre">
    <article class="carte">
      <h2>{{ titre }}</h2>
      <p>{{ auteur }} — {{ euros(prix) }}</p>
    </article>
  </template>
</body>
</html>

const { createApp } = Vue;

function euros(montant) {
  return montant.toLocaleString("fr-FR", { style: "currency", currency: "EUR" });
}

// Un composant est un objet d'options : son gabarit, ses props, son setup.
const CarteLivre = {
  template: "#modele-carte-livre",
  props: ["titre", "auteur", "prix"],
  setup() {
    return { euros };
  },
};

createApp({
  components: { CarteLivre },
}).mount("#app");

Illustration

Définition 5.1Composant

Un composant est un objet d'options qui décrit un morceau d'interface réutilisable : son gabarit (template), les données qu'il reçoit (props), les événements qu'il émet (emits) et sa logique (setup). Chaque fois qu'il est utilisé dans un gabarit, Vue en crée une instance, qui a son propre état et son propre morceau de DOM.

Trois choses sont nouvelles.

Proposition 5.2Nommer un composant

Un composant porte un nom de plusieurs mots (CarteLivre, pas Carte) : un nom d'un seul mot pourrait entrer en conflit avec un élément HTML présent ou futur (<header>, <search>…). Dans le code JavaScript on l'écrit en PascalCase ; dans un gabarit écrit dans la page HTML, en kebab-case.

La fonction setup du composant rend euros disponible dans son gabarit : chaque composant a sa propre portée. Le gabarit de la carte ne voit pas les données de l'application, et l'application ne voit pas celles de la carte. C'est voulu : un composant ne dépend que de ce qu'on lui donne explicitement, ce qui le rend réutilisable. Ce qu'on lui donne, ce sont ses props.

5.2 Les props : ce que le parent transmet

Définition 5.3Prop

Une prop (de l'anglais property) est une donnée qu'un composant parent transmet à un composant enfant, sous la forme d'un attribut de la balise de l'enfant. L'enfant la déclare dans son option props ; il la lit dans son gabarit par son nom, et dans setup par l'objet props reçu en premier argument.

Dans la première page, titre="Programmation Java" transmet une chaîne fixe, et :prix="32" une expression JavaScript : grâce aux deux-points (abréviation de v-bind, chapitre 2), la prop reçoit le nombre 32. Sans les deux-points, elle recevrait la chaîne "32". La différence est invisible à l'affichage, mais pas dans un calcul : "32" + 2 vaut "322".

La forme props: ["titre", "auteur", "prix"] se contente de nommer les props. Mieux vaut les décrire : leur type, si elles sont obligatoires, leur valeur par défaut, une règle de validité. Vue vérifie alors chaque utilisation et prévient en cas d'erreur.


const { createApp } = Vue;

const CarteLivre = {
  template: "#modele-carte-livre",
  props: {
    titre: { type: String, required: true },
    prix: {
      type: Number,
      required: true,
      validator: (valeur) => valeur >= 0,
    },
    stock: { type: Number, default: 0 },
  },
};

createApp({ components: { CarteLivre } }).mount("#app");

<div id="app">
  <carte-livre titre="Programmation Java" :prix="32" :stock="12"></carte-livre>
  <carte-livre titre="Analyse MPSI" prix="45"></carte-livre>
  <carte-livre :prix="-8"></carte-livre>
</div>

La console affiche :


[Vue warn]: Invalid prop: type check failed for prop "prix". Expected Number with value 45, got String with value "45".
  at <CarteLivre titre="Analyse MPSI" prix="45" >
  at <App>
[Vue warn]: Missing required prop: "titre"
  at <CarteLivre prix=-8 >
  at <App>
[Vue warn]: Invalid prop: custom validator check failed for prop "prix".
  at <CarteLivre prix=-8 >
  at <App>

Chaque avertissement indique la prop, la valeur reçue, et la position du composant dans l'arbre (at <CarteLivre …> at <App>). La page s'affiche quand même : la deuxième carte indique « 45 € — épuisé », puisque stock, non fourni, prend sa valeur par défaut, 0 ; la troisième, sans titre, affiche « -8 € — épuisé ». Vue ne corrige rien ; il prévient le développeur.

La suite du cours est sur Adloun

Le chapitre en entier, formules et figures comprises, avec le tuteur, les fiches, les QCM et les exercices.

Lire la suite du cours

En accès libre, sans inscription.

Exercices corrigés de ce chapitre

Lire la suite du cours →