Formation Vue.js : Chapitre 3 – Réactivité et gestion d’état
Nous poursuivons avec le Chapitre 3 de la formation Vue.js, incluant les notions théoriques, les exemples de code, et le résultat final de l’atelier fil rouge : la gestion d’un tableau de tâches avec ref() et l’affichage du nombre de tâches terminées via un computed().
Chapitre 3 : Réactivité et gestion d’état
L’objectif est d’apprendre à gérer des données dynamiques avec ref() et reactive(), à utiliser computed() pour dériver des valeurs, et à afficher une liste de tâches avec v-for.
Notions à apprendre
1. ref() vs reactive()
- `ref()` : pour les valeurs primitives (string, number, boolean) ou objets simples. Accès via `.value`
- `reactive()` : pour les objets complexes. Pas besoin de `.value` (mais attention aux problèmes de réactivité lors de la réaffectation)
Recommandé : Utiliser ref() pour les tableaux et objets simples, surtout en <script setup>.
2. computed()
- Crée une valeur dérivée, recalculée uniquement si ses dépendances changent
- Très utile pour les filtres, totaux, statistiques
3. v-for pour boucler sur une liste
- Syntaxe :
v-for="item in list" :key="item.id" - Toujours utiliser
:keypour une performance optimale et éviter les bugs de rendu
4. Mutation vs réactivité
- Vue détecte les mutations sur les tableaux/objets via
push(),splice(),item.property = value - Éviter :
array = newArray→ cela casse la réactivité. Utiliserarray.splice(0, array.length, ...newArray)ouref().value = newArray
Exemple de code : Gestion d’un tableau de tâches
<!-- src/App.vue (version simplifiée pour l’exemple) -->
<template>
<div class="app">
<h1>Mes tâches</h1>
<div class="stats">
<p>Total : {{ tasks.length }}</p>
<p>Terminées : {{ doneTasks.length }}</p>
</div>
<ul class="task-list">
<li v-for="task in tasks" :key="task.id" class="task-item">
{{ task.title }} — <span :class="task.status">{{ task.status }}</span>
</li>
</ul>
</div>
</template>
<script setup>
import { ref, computed } from 'vue'
// Tableau de tâches réactif
const tasks = ref([
{ id: 1, title: 'Apprendre Vue.js', status: 'done' },
{ id: 2, title: 'Créer un composant', status: 'todo' },
{ id: 3, title: 'Gérer l’état', status: 'inprogress' }
])
// Computed : liste des tâches terminées
const doneTasks = computed(() => {
return tasks.value.filter(task => task.status === 'done')
})
</script>
<style scoped>
.app {
padding: 2rem;
max-width: 600px;
margin: 0 auto;
}
.stats {
background: #f0f0f0;
padding: 1rem;
border-radius: 8px;
margin-bottom: 1rem;
display: flex;
gap: 1rem;
justify-content: space-between;
}
.task-list {
list-style: none;
padding: 0;
}
.task-item {
background: #fff;
border: 1px solid #ddd;
padding: 0.75rem;
margin: 0.5rem 0;
border-radius: 6px;
display: flex;
justify-content: space-between;
align-items: center;
}
.task-item .done {
color: #2ecc71;
font-weight: bold;
}
.task-item .todo {
color: #e74c3c;
}
.task-item .inprogress {
color: #f39c12;
}
</style>Atelier fil rouge : Afficher le nombre de tâches terminées
Objectif :
- Déclarer un tableau de tâches dans `App.vue` avec `ref()`
- Créer un `computed()` qui filtre les tâches avec `status === ‘done‘`
- Afficher le nombre total de tâches et le nombre de tâches terminées
- Afficher la liste des tâches avec leur statut
Résultat final : `App.vue` mis à jour
<!-- src/App.vue -->
<template>
<div class="app">
<header>
<h1>Mon gestionnaire de tâches</h1>
<p>Chapitre 3 : Réactivité et gestion d’état</p>
</header>
<main>
<div class="stats">
<p><strong>Total :</strong> {{ tasks.length }}</p>
<p><strong>Terminées :</strong> {{ doneTasks.length }}</p>
</div>
<div class="task-list">
<TaskCard
v-for="task in tasks"
:key="task.id"
:title="task.title"
:description="task.description"
:status="task.status"
/>
</div>
</main>
<footer>
<p>Vue.js — Formation Chapitre 3</p>
</footer>
</div>
</template>
<script setup>
import { ref, computed } from 'vue'
import TaskCard from './components/TaskCard.vue'
// Tableau de tâches réactif
const tasks = ref([
{ id: 1, title: 'Créer le composant TaskCard', description: 'Chapitre 2', status: 'done' },
{ id: 2, title: 'Gérer l’état avec ref()', description: 'Chapitre 3', status: 'todo' },
{ id: 3, title: 'Utiliser computed()', description: 'Chapitre 3', status: 'inprogress' }
])
// Computed : tâches terminées
const doneTasks = computed(() => {
return tasks.value.filter(task => task.status === 'done')
})
</script>
<style scoped>
.app {
font-family: Arial, sans-serif;
max-width: 600px;
margin: 2rem auto;
padding: 2rem;
border: 1px solid #ddd;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
header h1 {
color: #42b983;
margin-bottom: 0.5rem;
}
header p {
color: #666;
font-size: 0.9rem;
}
.stats {
background: #f0f0f0;
padding: 1rem;
border-radius: 8px;
margin-bottom: 1rem;
display: flex;
gap: 1rem;
justify-content: space-between;
}
.task-list {
display: flex;
flex-direction: column;
gap: 1rem;
}
footer {
margin-top: 2rem;
text-align: center;
font-size: 0.8rem;
color: #999;
}
</style>Explication du résultat
- Le tableau `tasks` est déclaré avec `ref()` → il est réactif.
- `doneTasks` est un `computed()` → il se met à jour automatiquement si `tasks` change.
- Les tâches sont affichées avec `v-for` → chaque `TaskCard` reçoit ses props (`title`, `description`, `status`).
- Le composant `TaskCard` est mis à jour pour afficher le statut (voir ci-dessous).
Mise à jour de `TaskCard.vue` (pour afficher le statut)
<!-- src/components/TaskCard.vue -->
<template>
<div class="task-card" :class="status">
<h3 class="task-title">{{ title }}</h3>
<p class="task-description">{{ description }}</p>
<span class="status-badge">{{ status }}</span>
</div>
</template>
<script setup>
defineProps({
title: {
type: String,
required: true
},
description: {
type: String,
default: 'Aucune description'
},
status: {
type: String,
default: 'todo',
validator: (value) => ['todo', 'inprogress', 'done'].includes(value)
}
})
</script>
<style scoped>
.task-card {
background: #f9f9f9;
border: 1px solid #ddd;
border-radius: 8px;
padding: 1rem;
margin: 0.5rem 0;
box-shadow: 0 2px 4px rgba(0,0,0,0.05);
transition: transform 0.2s;
position: relative;
}
.task-card:hover {
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
}
.task-title {
margin: 0 0 0.5rem 0;
color: #333;
font-size: 1.1rem;
}
.task-description {
margin: 0;
color: #666;
font-size: 0.9rem;
line-height: 1.4;
}
.status-badge {
position: absolute;
top: 0.5rem;
right: 0.5rem;
padding: 0.25rem 0.5rem;
border-radius: 12px;
font-size: 0.75rem;
font-weight: bold;
color: white;
}
.task-card.todo .status-badge {
background: #e74c3c;
}
.task-card.inprogress .status-badge {
background: #f39c12;
}
.task-card.done .status-badge {
background: #2ecc71;
}
</style>Testez-le vous-même !
- Mettez à jour
TaskCard.vueavec le code ci-dessus (ajout du statut). - Mettez à jour
App.vueavec le code du résultat final. - Lancez le serveur :
npm run dev
- Ouvrez `http://localhost:5173` → vous voyez :
- Le nombre total et terminé de tâches
- 3 cartes de tâches avec statut coloré
Prochain chapitre
Dans le chapitre Chapitre 4, nous aborderons la gestion des événements et les formulaires : vous apprendrez à créer un formulaire pour ajouter une nouvelle tâche, avec validation et gestion des événements.
Tu as maintenant une liste de tâches dynamique, avec un compteur de tâches terminées, et un composant TaskCard qui affiche le statut. C’est la base de toute application de gestion de tâches !