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 :key pour 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é. Utiliser array.splice(0, array.length, ...newArray) ou ref().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 !

  1. Mettez à jour TaskCard.vue avec le code ci-dessus (ajout du statut).
  2. Mettez à jour App.vue avec le code du résultat final.
  3. Lancez le serveur :
npm run dev
  1. 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 !


Claude BUENO

J’aide les équipes à développer leurs pratiques agiles et collaboratives. Je blogue depuis 2008 sur la transformation numérique, le développement d'applications web et mobile et les pratiques pour les réaliser dans les meilleures conditions. Sujets de prédilection : agilité, coaching, digital, management, marketing, développement web et mobile

Vous aimerez aussi...

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *