Cycle de vie & Watchers

Comprendre le comportement des composants Vue.js

Séance 5 — 25 février 2026

Cycle de vie Computed Watchers

Flèches pour naviguer

01 / Objectifs

Objectifs de la séance

Cycle de vie

Comprendre quand un composant naît, vit et meurt

Computed

Rappel des propriétés calculées avec mise en cache

Watchers

Réagir aux changements de données avec watch()

💡 Pour le projet Pokédex

Ces concepts sont essentiels : onMounted pour charger les Pokémon, computed pour filtrer la liste, et watch pour réagir à la recherche.

02 / Cycle de vie

La vie d'un composant

Un composant Vue passe par plusieurs étapes : création, montage dans le DOM, mises à jour, et destruction.

Vue nous permet d'exécuter du code à chaque étape grâce aux hooks de cycle de vie.

💡 Analogie

Comme un être vivant : naissance → vie → mort. À chaque étape, on peut intervenir.

Les 2 hooks les plus utilisés

1. onMounted() — Composant prêt dans le DOM
2. onUnmounted() — Composant supprimé du DOM

Hooks de cycle de vie

1

setup()

Le script s'exécute

2

onBeforeMount

Avant l'insertion dans le DOM

3

onMounted

Le composant est dans le DOM ✅

4

onBeforeUpdate

Avant une mise à jour

5

onUpdated

Après une mise à jour

6

onBeforeUnmount

Avant la suppression

7

onUnmounted

Le composant est détruit ✅

03 / Hook principal

onMounted()

App.vue
<script setup>
import { ref, onMounted } from 'vue'

const users = ref([])
const loading = ref(true)

onMounted(async () => {
  // Le composant est dans le DOM
  // Idéal pour charger des données
  const response = await fetch('/api/users')
  users.value = await response.json()
  loading.value = false
})
</script>

<template>
  <p v-if="loading">Chargement...</p>
  <ul v-else>
    <li v-for="user in users">
      {{ user.name }}
    </li>
  </ul>
</template>

Cas d'utilisation

🌐 Charger des données

Appeler une API pour récupérer des données au chargement

📚 Initialiser une librairie

Carte, graphique, éditeur qui ont besoin du DOM

👂 Ajouter un écouteur

Écouter le scroll, le resize, les touches clavier

🎯 Accéder au DOM

Lire des dimensions, mettre le focus sur un input

⚠️ Le DOM n'existe PAS dans setup()

Il faut attendre onMounted pour accéder aux éléments HTML

Tutoriel Vue.js — Étape 8 →
04 / Nettoyage

onUnmounted()

ScrollTracker.vue
<script setup>
import { ref, onMounted, onUnmounted }
  from 'vue'

const scrollY = ref(0)

function handleScroll() {
  scrollY.value = window.scrollY
}

// Ajouter l'écouteur
onMounted(() => {
  window.addEventListener(
    'scroll', handleScroll
  )
})

// Supprimer l'écouteur
onUnmounted(() => {
  window.removeEventListener(
    'scroll', handleScroll
  )
})
</script>

<template>
  <p>Scroll : {{ scrollY }}px</p>
</template>

Quand un composant est supprimé du DOM (navigation, v-if), il faut nettoyer ce qu'on a mis en place.

Pourquoi nettoyer ?

🧠 Fuites mémoire

Les écouteurs non supprimés restent actifs en arrière-plan

⚡ Performances

Chaque listener inutile consomme des ressources

🐛 Bugs

Du code qui tourne sur un composant qui n'existe plus

💡 Règle d'or

Chaque addEventListener dans onMounted doit avoir son removeEventListener dans onUnmounted

05 / Rappel

Propriétés calculées

Sans computed

// Recalculé à chaque rendu ❌
function getTotal() {
  return items.value
    .reduce((sum, i) =>
      sum + i.price, 0
    )
}

Dans le template : {{ getTotal() }}

Recalculé à chaque rendu, même si rien n'a changé

Avec computed

// Mis en cache automatiquement ✅
const total = computed(() => {
  return items.value
    .reduce((sum, i) =>
      sum + i.price, 0
    )
})

Dans le template : {{ total }}

Recalculé uniquement si items change

🧠 Cache intelligent

Ne recalcule que si ses dépendances changent

📖 Lecture seule

Retourne une valeur, pas une action

📋 Dans le template

{{ total }} au lieu de {{ getTotal() }}

06 / Watchers

watch() — Réagir aux changements

SearchPage.vue
<script setup>
import { ref, watch } from 'vue'

const search = ref('')
const results = ref([])

// Surveiller 'search' et réagir
watch(search, async (newVal, oldVal) => {
  console.log(
    `"${oldVal}" → "${newVal}"`
  )

  if (newVal.length >= 3) {
    const res = await fetch(
      `/api/search?q=${newVal}`
    )
    results.value = await res.json()
  }
})
</script>

<template>
  <input v-model="search"
         placeholder="Rechercher..." />
  <ul>
    <li v-for="r in results">
      {{ r.name }}
    </li>
  </ul>
</template>

Points clés

1 Source explicite

watch(search, ...) — on déclare ce qu'on surveille

2 Deux arguments

Reçoit la nouvelle et l'ancienne valeur

3 Effets de bord

Idéal pour : appels API, localStorage, logs

4 Pas immédiat

Ne se déclenche PAS au premier rendu (par défaut)

Option : { immediate: true }

Pour exécuter le watcher dès le chargement du composant

Tutoriel Vue.js — Étape 10 →
07 / watchEffect

watchEffect() — Détection automatique

watch() Explicite

watch(search, async (val) => {
  results.value =
    await fetchAPI(val)
})
On déclare ce qu'on surveille
Accès à oldValue et newValue
Ne s'exécute pas immédiatement

watchEffect() Automatique

watchEffect(async () => {
  results.value =
    await fetchAPI(search.value)
})
Détecte les dépendances automatiquement
Pas d'accès à oldValue
S'exécute immédiatement

💡 Conseil

En cas de doute, utilisez watch() — c'est plus explicite et prévisible. Vous savez exactement ce qui est surveillé.

08 / Comparaison

computed vs watch

computed()

Transformer des données

"Combien de Pokémon de type Feu ?"

const firePokemons = computed(() =>
  pokemons.value.filter(
    p => p.type === 'Feu'
  )
)
Retourne une VALEUR
watch()

Réagir à un changement

"Quand la recherche change, appeler l'API"

watch(search, async (val) => {
  const res = await fetch(
    `/api?q=${val}`
  )
  results.value = await res.json()
})
Exécute un EFFET

Comment choisir ?

🧮

J'ai besoin d'une VALEUR dérivée

computed

filteredPokemons, totalCount, pokemonsByType

J'ai besoin d'un EFFET

watch

appel API, sauvegarde localStorage, log

⚠️ Erreur fréquente

Ne pas utiliser watch pour calculer une valeur dérivée — c'est le rôle de computed.

09 / Pratique

À vous de jouer !

📋 En classe

Tutoriel étapes 8-10 ensemble, puis exercices 5 à 7

📚 Devoirs

Terminer les exercices 5, 6 et 7 pour la prochaine séance

10 / Pour aller plus loin

Ressources

Prochaine séance

Introduction à Vuetify !

Composants Material Design, grilles et layouts