La Réactivité Vue : ref() et Données Dynamiques
Tes composants sont des cadavres statiques pour l'instant. Ils affichent des trucs, ok, mais ils ne réagissent à rien. Clique sur un bouton ? Rien. Survole un élément ? Nada. C'est triste. 💀
On va leur donner vie avec ref(), le cœur battant de Vue.
Objectifs du chapitre
À la fin de ce chapitre, tu seras capable de :
- ✅ Comprendre ce qu'est la réactivité dans Vue
- ✅ Créer des données réactives avec
ref() - ✅ Modifier ces données en réponse à des actions utilisateur
- ✅ Maîtriser la règle
.value(script) vs sans.value(template)
C'est quoi la réactivité ?

En JavaScript classique, quand tu changes une variable, il ne se passe rien à l'écran. Le navigateur ne sait pas qu'il doit mettre à jour l'affichage.
// JavaScript classique
let compteur = 0
compteur++
// L'écran affiche toujours 0... 😢
Avec Vue et ref(), c'est différent. Vue surveille la donnée. Dès qu'elle change, Vue met à jour l'affichage automatiquement.
// Vue avec ref()
const compteur = ref(0)
compteur.value++
// L'écran affiche 1 instantanément ! ✨
C'est ça la réactivité : Vue réagit aux changements de données et synchronise l'affichage tout seul.
Créer une ref()

Syntaxe de base
<script setup>
import { ref } from 'vue'
// Créer des refs avec différents types de valeurs
const compteur = ref(0) // Number
const message = ref('Hello') // String
const isVisible = ref(true) // Boolean
const tags = ref(['Vue', 'CSS']) // Array
</script>
Deux étapes obligatoires :
- Importer
refdepuis'vue' - Appeler
ref()avec ta valeur initiale
| Type | Exemple | Résultat |
|---|---|---|
| Number | ref(0) |
Compteur, quantité, index |
| String | ref('Hello') |
Texte, message, nom |
| Boolean | ref(true) |
État on/off, visible/caché |
| Array | ref([]) |
Liste d'éléments |
| Object | ref({}) |
Données structurées |
La règle d'or : .value

C'est LA source d'erreur numéro 1 avec ref(). Grave ça dans ton cerveau :
| Contexte | Syntaxe | Pourquoi |
|---|---|---|
Dans <script> |
Avec .value |
Tu accèdes à la vraie valeur |
Dans <template> |
Sans .value |
Vue déballe automatiquement |
Exemple concret
<script setup>
import { ref } from 'vue'
const compteur = ref(0)
function incrementer() {
// ✅ Dans le script : .value obligatoire
compteur.value++
// ❌ FAUX : ça ne marchera pas
// compteur++
}
function afficherValeur() {
// ✅ Pour lire aussi, .value
console.log(compteur.value)
}
</script>
<template>
<!-- ✅ Dans le template : PAS de .value -->
<p>Compteur : {{ compteur }}</p>
<!-- ❌ FAUX : ça afficherait l'objet ref, pas la valeur -->
<!-- <p>{{ compteur.value }}</p> -->
<button @click="incrementer">+1</button>
</template>
Erreurs fréquentes
| ❌ Erreur | ✅ Correction |
|---|---|
compteur++ dans script |
compteur.value++ |
{ { compteur.value }} dans template |
{ { compteur }} |
if (isVisible) dans script |
if (isVisible.value) |
| Oublier l'import | import { ref } from 'vue' |
Modifier une ref avec des événements

Pour que quelque chose se passe quand l'utilisateur interagit, tu as besoin de :
- Un événement (
@click,@mouseover, etc.) - Une fonction qui modifie la ref
- Vue s'occupe du reste !
Les événements de base
| Événement | Déclencheur | Exemple |
|---|---|---|
@click |
Clic souris | Bouton, lien |
@mouseover |
Survol | Tooltip, preview |
@mouseout |
Quitte l'élément | Fermer tooltip |
@keyup |
Touche relâchée | Recherche live |
@keyup.enter |
Touche Entrée | Valider formulaire |

Exemple : Toggle visibility
Un pattern ultra-courant : afficher/cacher un élément.
<script setup>
import { ref } from 'vue'
const isMenuOpen = ref(false)
function toggleMenu() {
isMenuOpen.value = !isMenuOpen.value
}
</script>
<template>
<div>
<button @click="toggleMenu" class="bg-blue-500 text-white px-4 py-2 rounded">
{{ isMenuOpen ? 'Fermer' : 'Ouvrir' }} le menu
</button>
<nav v-if="isMenuOpen" class="mt-4 p-4 bg-gray-100 rounded">
<a href="#" class="block py-2">Accueil</a>
<a href="#" class="block py-2">Projets</a>
<a href="#" class="block py-2">Contact</a>
</nav>
</div>
</template>
Décortiquons :
isMenuOpencommence àfalse(menu fermé)- Au clic,
toggleMenu()inverse la valeur (false→true) - Vue voit le changement et re-render
v-if="isMenuOpen"affiche maintenant le menu- Le texte du bouton change aussi grâce au ternaire


Exemple : Compteur avec +/-
<script setup>
import { ref } from 'vue'
const count = ref(0)
function increment() {
count.value++
}
function decrement() {
count.value--
}
function reset() {
count.value = 0
}
</script>
<template>
<div class="flex items-center gap-4">
<button @click="decrement" class="bg-red-500 text-white w-10 h-10 rounded-full text-xl">
-
</button>
<span class="text-4xl font-bold w-16 text-center">{{ count }}</span>
<button @click="increment" class="bg-green-500 text-white w-10 h-10 rounded-full text-xl">
+
</button>
<button @click="reset" class="bg-gray-400 text-white px-4 py-2 rounded ml-4">
Reset
</button>
</div>
</template>

Affichage conditionnel : v-if

Maintenant que tu sais créer des refs booléennes (ref(true) / ref(false)), tu peux afficher ou cacher des éléments selon leur valeur.
v-if : Afficher si vrai
<script setup>
import { ref } from 'vue'
const isLoggedIn = ref(false)
</script>
<template>
<div v-if="isLoggedIn">
Bienvenue ! Tu es connecté.
</div>
</template>
Comportement : Si isLoggedIn est false, l'élément n'existe pas dans le DOM. Il est complètement supprimé.

v-else : L'alternative
<template>
<div v-if="isLoggedIn">
Bienvenue ! Tu es connecté.
</div>
<div v-else>
Connecte-toi pour continuer.
</div>
</template>
⚠️ Important : v-else doit être immédiatement après un v-if. Pas d'élément entre les deux !

v-else-if : Plusieurs conditions
<script setup>
import { ref } from 'vue'
const status = ref('loading') // 'loading', 'success', 'error'
</script>
<template>
<div v-if="status === 'loading'" class="text-blue-500">
⏳ Chargement...
</div>
<div v-else-if="status === 'success'" class="text-green-500">
✅ Succès !
</div>
<div v-else-if="status === 'error'" class="text-red-500">
❌ Erreur !
</div>
<div v-else class="text-gray-500">
🤷 État inconnu
</div>
</template>

v-if vs v-show : Quelle différence ?
| Directive | Comportement | Utilisation |
|---|---|---|
v-if |
Supprime l'élément du DOM | Changements rares |
v-show |
Cache avec display: none |
Changements fréquents |
<template>
<!-- v-if : l'élément est SUPPRIMÉ si false -->
<div v-if="isVisible">Je suis dans le DOM ou pas</div>
<!-- v-show : l'élément est CACHÉ mais reste dans le DOM -->
<div v-show="isVisible">Je suis toujours là, juste invisible</div>
</template>
Règle simple :
- Menu qui s'ouvre/ferme souvent →
v-show - Contenu qui change rarement (login/logout) →
v-if - Pas sûr ? →
v-if(c'est le défaut)
Exemple complet : Alert dismissible
<script setup>
import { ref } from 'vue'
const showAlert = ref(true)
function dismissAlert() {
showAlert.value = false
}
</script>
<template>
<div
v-if="showAlert"
class="bg-blue-100 text-blue-800 p-4 rounded-lg flex justify-between items-center"
>
<span>ℹ️ Ceci est une information importante !</span>
<button @click="dismissAlert" class="text-blue-600 hover:text-blue-800">
✕
</button>
</div>
</template>

Exercices pratiques
Exercice 1 : Toggle Show/Hide ⭐
Objectif : Maîtriser le pattern toggle de base

Ta mission : Créer un composant Spoiler.vue qui masque/révèle du contenu.
Comportement :
- Par défaut, le contenu est caché
- Un bouton "Voir le spoiler" / "Masquer" toggle l'affichage
- Le texte du bouton change selon l'état
Props à implémenter :
| Prop | Type | Description |
|---|---|---|
title |
String | Titre affiché (ex: "Spoiler saison 3") |
content |
String | Le contenu caché |
Exemple d'utilisation :
<Spoiler
title="Fin du film"
content="Le majordome était le coupable depuis le début."
/>
Exercice 2 : Bouton Like ⭐⭐
Objectif : Combiner toggle, compteur et classes dynamiques

Ta mission : Créer un composant LikeButton.vue avec :
- Un cœur cliquable (🤍 gris / ❤️ rouge)
- Un compteur de likes
- Au clic : toggle like + mise à jour compteur
Props à implémenter :
| Prop | Type | Description |
|---|---|---|
initialCount |
Number | Nombre de likes initial (défaut: 0) |
Comportement :
- Clic 1 : cœur rouge, compteur +1
- Clic 2 : cœur gris, compteur -1
- Style différent si liké (bordure rouge, fond rose clair)
Exemple d'utilisation :
<LikeButton :initialCount="42" />
Exercice 3 : Navigation Responsive ⭐⭐
Objectif : Créer un menu hamburger animé


Ta mission : Créer un composant NavBar.vue avec :
- Un logo à gauche
- Des liens de navigation (desktop : visibles, mobile : cachés)
- Un bouton hamburger (☰) visible uniquement sur mobile
- Au clic : le menu s'ouvre/ferme avec une animation
Props à implémenter :
| Prop | Type | Description |
|---|---|---|
logo |
String | Texte du logo |
links |
Array | Liste des liens [{ label: 'Accueil', href: '/' }, ...] |
Comportement :
- Desktop (>768px) : liens en ligne, pas de hamburger
- Mobile : hamburger visible, menu caché par défaut
- Clic hamburger : menu slide de haut en bas
- Icône toggle : ☰ (fermé) → ✕ (ouvert)
Exemple d'utilisation :
<NavBar
logo="MonSite"
:links="[
{ label: 'Accueil', href: '/' },
{ label: 'Projets', href: '/projets' },
{ label: 'Contact', href: '/contact' }
]"
/>
Exercice 4 : Notation 5 étoiles ⭐⭐⭐
Objectif : Gérer le hover et le clic sur une liste

Ta mission : Créer un composant StarRating.vue avec :
- 5 étoiles cliquables (☆ vide / ★ pleine)
- Au survol : preview de la note
- Au clic : la note est enregistrée
- Affichage de la note actuelle
Props à implémenter :
| Prop | Type | Description |
|---|---|---|
initialRating |
Number | Note initiale (0-5, défaut: 0) |
Comportement :
- Hover sur étoile 3 : étoiles 1, 2, 3 se remplissent (preview)
- Mouse out : revient à la note enregistrée
- Clic sur étoile 3 : note = 3 (enregistré)
- Les étoiles remplies sont jaunes/dorées
Exemple d'utilisation :
<StarRating :initialRating="3" />
Exercice BONUS : Card Projet Complète ⭐⭐⭐
Objectif : Combiner tous les concepts dans un composant réaliste

Ta mission : Reprendre ton ProjectCard.vue du chapitre précédent et y ajouter :
- Le composant
LikeButtonintégré - Le composant
StarRatingpour noter le projet - Un bouton "Voir détails" qui toggle une description longue (comme
Spoiler)
Exemple d'utilisation :
<ProjectCard
title="Café Bohème"
description="Site vitrine pour un restaurant"
image="/images/cafe.jpg"
:tags="['Vue', 'Tailwind']"
:initialLikes="42"
:initialRating="4"
/>
La suite...
Tu sais maintenant créer des données réactives et les modifier. Mais pour l'instant, c'est toi (le code) qui décide quand ça change.
Dans le prochain chapitre, on verra v-model : l'utilisateur tape dans un input, et la donnée se met à jour automatiquement. C'est la magie du two-way binding, et c'est ce qui rend les formulaires Vue si agréables à coder. (ง'̀-'́)ง