Comprendre components dans Vue.js
Avec la montée en complexité de nos applications web, certains blocs d’interface deviennent répétitifs ou difficiles à maintenir. Afficher une modale, intégrer un lecteur, gérer des actions utilisateur… ces éléments suivent souvent la même structure, tout en nécessitant une logique propre.
Vue.js propose pour cela une approche modulaire fondée sur les composants : des blocs réutilisables, configurables, que l’on peut insérer et orchestrer dans toute l’interface. Ils permettent de clarifier le code, de structurer l’affichage, et de séparer les responsabilités entre parties de l’interface. Pour illustrer cela, notre application vidéo utilise déjà deux composants globaux : lightbox, pour afficher une image en surimpression, et video-lightbox, pour lire une vidéo dans une modale. Nous allons analyser ce type de structure, comprendre comment la déclarer, et voir comment elle s’intègre naturellement dans la logique de Vue.js.
Pourquoi structurer en composants ?
Plus une interface devient riche, plus elle a besoin d’ordre. Si tout est regroupé dans un seul bloc app, on finit vite par perdre le fil : les données, les événements, les fonctions et les éléments visuels se mélangent, rendant l’ensemble difficile à lire et à maintenir.
Les composants permettent justement de découper l’interface en briques autonomes. Chaque élément a son rôle, son nom, ses propres données, et peut être utilisé à plusieurs endroits sans avoir à dupliquer sa logique.
Dans notre application vidéo, par exemple, nous avons séparé l’affichage des images et des vidéos dans deux composants distincts. Cela évite de recopier à chaque fois la logique d’ouverture, de fermeture ou d’animation. Si demain nous voulons ajouter un composant “fiche d’artiste” ou “formulaire de contact”, nous pourrons le faire sans alourdir le reste de l’interface.
C’est une manière de penser l’interface comme une construction modulaire, évolutive, et facile à faire grandir.
Déclarer un composant avec app.component()
Dans Vue.js, un composant peut être déclaré globalement grâce à la méthode app.component(). On lui attribue un nom — qui deviendra le nom de balise utilisé dans le HTML — et on lui associe une définition contenant son template, ses props, et éventuellement sa logique interne.
Cette déclaration crée un bloc indépendant, utilisable dans n’importe quelle partie de l’interface. C’est une excellente manière d’isoler les éléments récurrents comme des cartes, des formulaires ou des modales.
Prenons un exemple simple extrait de notre projet. Nous avons défini un composant lightbox pour afficher une image en surimpression, avec un bouton de fermeture :
app.component('lightbox', {
props: ['src', 'visible'],
template: `
<div v-if="visible" class="fixed inset-0 bg-black bg-opacity-75 flex items-center justify-center z-50">
<div class="relative">
<img :src="src" class="max-w-full max-h-full">
<button @click="$emit('close')" class="absolute top-2 right-2 text-white text-2xl">×</button>
</div>
</div>
`,
});Ce composant reçoit deux propriétés du parent : src pour l’image à afficher, et visible pour contrôler sa présence. Lorsqu’on clique sur la croix, le composant émet un événement personnalisé close, que le parent peut écouter pour agir.
Le nom du composant est ici écrit en kebab-case (lightbox). Il correspond exactement à la manière dont on l’utilisera dans l’interface HTML, sous forme de balise : <lightbox>.
Utiliser un composant dans le HTML de l’application
Une fois déclaré avec app.component(), un composant global peut être utilisé comme n’importe quelle balise HTML. Vue reconnaît son nom, lui transmet les données spécifiées dans les props, et permet d’écouter les événements qu’il émet.
Dans notre exemple, le composant lightbox s’utilise ainsi :
<lightbox
:src="selectedImage"
:visible="lightboxVisible"
@close="closeLightbox">
</lightbox>Ici, nous transmettons deux props :
srccontient le lien de l’image à afficher,visibledétermine si la modale doit être visible ou non.
Et nous écoutons un événement close déclenché depuis le composant pour exécuter la méthode closeLightbox() dans le composant parent. Ce lien entre parent et enfant est au cœur de la logique Vue : les props vont vers le bas, les événements remontent vers le haut.
Ce principe permet de garder chaque composant concentré sur sa tâche. Le composant lightbox ne décide pas de s’afficher ou de se cacher : il expose un comportement (@close) et laisse le parent gérer l’état global (lightboxVisible).
Cette séparation favorise la réutilisation. On pourrait utiliser ce même composant dans une galerie, une fiche produit ou une interface d’administration, simplement en adaptant les données transmises.
Communication entre composants : props, événements, écoute
Dans Vue.js, la communication entre un composant parent et un composant enfant repose sur deux mécanismes complémentaires : les props et les événements personnalisés. Ce duo permet de transmettre des données vers le bas, et de faire remonter des actions vers le haut.
Un composant enfant reçoit ses données depuis le parent grâce aux props. Ce sont des propriétés déclarées explicitement dans la définition du composant, que l’on peut ensuite utiliser dans le template ou dans la logique interne.
Prenons l’exemple de notre video-lightbox :
app.component('video-lightbox', {
props: ['src', 'visible'],
template: `
<div v-if="visible" class="...">
<div class="relative">
<button @click="$emit('close')" class="...">×</button>
<div class="relative w-full aspect-video p-4">
<iframe :src="src" class="w-full h-full" frameborder="0" allow="autoplay" allowfullscreen></iframe>
</div>
</div>
</div>
`
});Ce composant ne fait aucun choix de lui-même. Il affiche ou non la modale selon la valeur de visible, et lit une vidéo en fonction de src. C’est le parent qui décide quoi afficher et quand.
En retour, l’enfant peut émettre un événement vers le parent avec $emit. Ici, lorsqu’on clique sur le bouton de fermeture, l’événement close est déclenché. Le parent peut alors écouter cet événement et exécuter une méthode :
<video-lightbox
:src="selectedVideo"
:visible="videoLightboxVisible"
@close="closeVideoLightbox">
</video-lightbox>Ce modèle rend chaque composant prévisible et réutilisable. Il ne connaît pas l’ensemble de l’application : il s’occupe uniquement de ce qu’on lui demande. Le reste — comme l’état général, le choix de la vidéo ou le retour à l’accueil — reste sous la responsabilité du composant parent.
Bonnes pratiques de découpage et de réutilisation
Créer un composant est simple, mais encore faut-il savoir quand le faire. Si chaque balise devient un composant, l’interface devient illisible. Si tout est regroupé dans un seul bloc, elle devient vite ingérable. Le bon équilibre consiste à isoler les éléments qui ont une logique propre, qu’on souhaite réutiliser, ou qui alourdissent la lisibilité du HTML.
Un bon candidat pour devenir composant :
- s’affiche dans plusieurs endroits différents,
- contient une logique de présentation ou d’interaction autonome,
- doit pouvoir évoluer sans modifier tout le reste de l’application.
Prenons l’exemple du video-lightbox. Il combine un iframe, un bouton de fermeture, une animation, et une gestion d’état. En l’extrayant, nous clarifions le HTML principal tout en rendant ce bloc réutilisable pour d’autres vidéos, ou dans un autre contexte (ex. galerie privée, tutoriels, portfolio…).
Autre cas typique : un bouton avec des effets visuels, une icône, et un comportement au clic. Plutôt que de recopier son HTML partout, on peut en faire un composant app-button configuré avec une prop label, un slot pour le contenu, ou un type d’action. Cela évite les doublons et centralise le style et les événements.
Ce découpage progressif permet aussi d’anticiper les évolutions. Si demain nous voulons ajouter un effet de transition à la lightbox, changer l’icône du bouton, ou intégrer un lecteur Vimeo à côté de YouTube, ces modifications resteront localisées dans un seul fichier, sans perturber le reste de l’interface.
Composant local vs composant global
Vue.js permet de déclarer un composant de deux manières : globalement avec app.component() ou localement dans un composant parent. Ces deux approches sont complémentaires, et le choix dépend de la portée du composant.
Un composant global peut être utilisé dans n’importe quelle partie de l’application. C’est le cas de lightbox ou video-lightbox dans notre projet : nous les avons déclarés dès le démarrage avec app.component(...), ce qui nous permet de les insérer librement où nous voulons, sans avoir à les réimporter ou les redéfinir ailleurs.
Mais certains composants ne sont utiles que dans un seul contexte. Imaginons une modale “fiche d’artiste” que l’on affiche uniquement dans une page spécifique. Dans ce cas, il est plus pertinent de déclarer ce composant localement, directement dans le bloc components du composant parent :
components: {
// On indique ici les composants que l’on veut utiliser localement dans ce fichier
ArtistModal
}Cela suppose que le composant ArtistModal a été importé en haut du fichier, généralement comme ceci :
import ArtistModal from './components/ArtistModal.js';
// Ou, si l’on utilise des fichiers .vue
import ArtistModal from './components/ArtistModal.vue';
// Le fichier ArtistModal.js pourrait ressembler à ceci :
export default {
name: 'ArtistModal',
props: ['artist', 'visible'],
emits: ['close'],
template: `
<div v-if="visible" class="fixed inset-0 bg-black bg-opacity-60 flex items-center justify-center z-50">
<div class="bg-white p-6 rounded-lg shadow-lg relative max-w-md w-full">
<button @click="$emit('close')"
class="absolute top-2 right-2 text-gray-500 hover:text-black text-2xl">×</button>
<h2 class="text-xl font-semibold mb-4">{{ artist.name }}</h2>
<p class="text-gray-700">{{ artist.description }}</p>
</div>
</div>
`
};Cette méthode garde l’interface plus propre, évite d’encombrer la liste des composants globaux, et permet d’isoler certains comportements spécifiques à une section de l’application.
La règle générale est simple :
- globaux pour les composants partagés (bouton, modale, carte),
- locaux pour les composants spécifiques à une zone ou une page.
Dans une architecture bien pensée, on combine les deux. Les composants globaux constituent la “boîte à outils” réutilisable ; les composants locaux organisent le contenu au cas par cas.
Et après ? Vers les composants .vue et l’organisation en dossiers
Lorsque l’application commence à s’étoffer, déclarer tous ses composants dans le même fichier JavaScript devient vite difficile à maintenir. C’est là qu’interviennent les Single File Components, ou fichiers .vue.
Un fichier .vue regroupe au même endroit le template, le script JavaScript, et les styles associés à un composant. Cela permet de mieux organiser le code, de retrouver facilement les éléments liés à une fonctionnalité, et de simplifier la maintenance au fil du temps.
Voici à quoi ressemble un composant dans ce format :
<template>
<div class="modal">
<h2>{{ title }}</h2>
<button @click="$emit('close')">Fermer</button>
</div>
</template>
<script>
export default {
props: ['title']
}
</script>
<style scoped>
.modal {
background: white;
padding: 2rem;
}
</style>Chaque composant devient un fichier autonome, situé dans un dossier components/, importé ensuite dans le script principal ou dans un composant parent. Cela ne change rien au fonctionnement : c’est simplement une manière plus propre de structurer l’interface.
Cette organisation est particulièrement utile dans les projets à long terme, ou dès qu’un composant dépasse quelques lignes. Elle rend aussi plus facile la collaboration, la documentation, ou l’intégration avec des outils comme Tailwind CSS, un routeur, ou une gestion d’état.
Dans nos prochains articles, nous continuerons d’utiliser les composants déclarés via app.component() pour garder les choses simples. Mais si vous vous sentez à l’aise, rien ne vous empêche de commencer à explorer cette structure en fichiers .vue — Vue est conçu pour évoluer avec vous, pas contre vous.
Conclusion
Les composants sont au cœur de la logique de Vue.js. Ils permettent de construire une interface claire, modulaire, et évolutive, en séparant les responsabilités et en évitant les répétitions. Que ce soit pour une simple modale, une fiche, une carte ou un formulaire, chaque bloc peut devenir une unité autonome, réutilisable et maintenable.
Nous avons vu comment les déclarer, les utiliser, les configurer avec des props, et les relier à leur parent par des événements. Nous avons aussi distingué les composants globaux, pratiques pour les éléments transversaux, et les composants locaux, mieux adaptés à des usages ciblés.
