1. v-if / v-else-if / v-else — Rendu conditionnel

v-if ajoute ou supprime réellement l'élément du DOM selon la condition. Si la condition est fausse, l'élément n'existe tout simplement pas dans le DOM.

<div id="app">
  <!-- v-if simple -->
  <p v-if="estConnecte">Bienvenue, {{ prenom }} !</p>
  <p v-else>Veuillez vous connecter.</p>

  <!-- Chaîne v-if / v-else-if / v-else -->
  <div v-if="note >= 16">🏆 Très bien</div>
  <div v-else-if="note >= 12">✅ Bien</div>
  <div v-else-if="note >= 10">⚠️ Passable</div>
  <div v-else>❌ Insuffisant</div>

  <!-- v-if sur un groupe avec <template> (sans div wrapper) -->
  <template v-if="chargement">
    <p>Chargement...</p>
    <div class="spinner"></div>
  </template>
  <div v-else>Contenu chargé</div>
</div>

<script>
createApp({
  data() {
    return {
      estConnecte: true,
      prenom: 'Alice',
      note: 14,
      chargement: false
    };
  }
}).mount('#app');
</script>
✅ Utilise <template v-if> pour conditionner un groupe d'éléments sans ajouter un <div> inutile dans le DOM.
⚠️ v-else et v-else-if doivent immédiatement suivre un élément v-if ou v-else-if — pas d'éléments intermédiaires.

v-if + v-for : priorité

En Vue 3, v-if a une priorité plus haute que v-for (contrairement à Vue 2). Ne les mets jamais sur le même élément. Préfère un <template v-for> avec un v-if à l'intérieur.

<!-- ❌ Mauvaise pratique : v-if + v-for sur le même élément -->
<li v-for="item in items" v-if="item.actif">{{ item.nom }}</li>

<!-- ✅ Bonne pratique -->
<template v-for="item in items" :key="item.id">
  <li v-if="item.actif">{{ item.nom }}</li>
</template>

2. v-show — Visibilité CSS

v-show cache l'élément via display: none mais le laisse dans le DOM. L'élément est toujours rendu, juste invisible.

<!-- v-show ajoute/enlève display:none -->
<div v-show="panneauOuvert">
  Contenu du panneau (toujours dans le DOM)
</div>
<button @click="panneauOuvert = !panneauOuvert">Toggle</button>

<!-- Équivalent CSS de ce que fait Vue : -->
<!-- <div style="display: none;">...</div> -->

v-if vs v-show — Choisir le bon

Critère v-if v-show
DOM Ajouté / supprimé Toujours présent
Premier rendu Rapide si false Toujours rendu
Toggle fréquent Coûteux Économique
Utilisation idéale Conditions stables (auth, rôles) Menus, accordéons, tooltips
<!-- Menu dropdown : toggle fréquent → v-show -->
<ul v-show="menuOuvert" class="dropdown">...</ul>

<!-- Zone admin : condition stable → v-if -->
<section v-if="utilisateur.estAdmin">
  <h2>Panneau Admin</h2>
</section>
💡 En cas de doute : commence par v-if. Si les performances deviennent un problème avec de nombreux toggles, passe à v-show.

3. v-for — Rendu de listes

v-for répète un élément pour chaque item d'un tableau ou d'un objet. L'attribut :key est obligatoire et doit être unique.

<!-- Itérer un tableau simple -->
<ul>
  <li v-for="fruit in fruits" :key="fruit">{{ fruit }}</li>
</ul>

<!-- Avec l'index -->
<ul>
  <li v-for="(fruit, index) in fruits" :key="index">
    {{ index + 1 }}. {{ fruit }}
  </li>
</ul>

<!-- Tableau d'objets (clé = id unique) -->
<div v-for="tache in taches" :key="tache.id" class="tache-card">
  <h3>{{ tache.titre }}</h3>
  <span>{{ tache.statut }}</span>
</div>

<!-- Itérer un objet -->
<dl>
  <template v-for="(valeur, cle) in config" :key="cle">
    <dt>{{ cle }}</dt>
    <dd>{{ valeur }}</dd>
  </template>
</dl>

<!-- Générer une plage de nombres -->
<span v-for="n in 5" :key="n">{{ n }} </span>
<!-- Affiche : 1 2 3 4 5 -->

Pourquoi :key est crucial

Sans :key, Vue réutilise les éléments DOM dans l'ordre, ce qui cause des bugs visuels lors des tris, filtres ou suppressions. Avec un :key unique (id de base de données, valeur unique), Vue sait exactement quel élément correspond à quel item.

<!-- ❌ Pas de key → bugs potentiels -->
<li v-for="item in items">{{ item.nom }}</li>

<!-- ❌ Index comme key → problèmes si la liste est triée/filtrée -->
<li v-for="(item, i) in items" :key="i">{{ item.nom }}</li>

<!-- ✅ ID unique → parfait -->
<li v-for="item in items" :key="item.id">{{ item.nom }}</li>

Méthodes de mutation réactives

Vue détecte les mutations de tableau via ces méthodes :

// ✅ Ces méthodes déclenchent la réactivité Vue :
this.items.push(nouveauItem);
this.items.pop();
this.items.shift();
this.items.unshift(item);
this.items.splice(index, 1);
this.items.sort();
this.items.reverse();

// ✅ Remplacer le tableau (aussi réactif) :
this.items = this.items.filter(i => i.actif);
this.items = [...this.items, nouveauItem]; // spread

// ❌ Mutation directe par index (non réactive en Options API) :
// this.items[0] = { nom: 'nouveau' }; // Ne pas faire !
✅ Pour modifier un élément par index en Options API : this.items.splice(index, 1, nouvelItem) — ou utiliser la Composition API avec reactive().

4. v-bind — Classe et style dynamiques

v-bind (raccourci :) lie des attributs HTML à des expressions JavaScript. Son utilisation avec :class et :style est particulièrement puissante.

:class — Classe dynamique

<!-- Objet : { nom-classe: condition } -->
<div :class="{ actif: estActif, erreur: aDesErreurs, 'texte-grand': grandMode }"></div>

<!-- Tableau : plusieurs classes -->
<div :class="[classeBase, classeConditionnelle, { 'en-ligne': modeEnLigne }]"></div>

<!-- Expression directe (ternaire) -->
<button :class="estValide ? 'btn-succes' : 'btn-erreur'">OK</button>

<!-- Combiner class statique et :class dynamique -->
<div class="carte" :class="{ 'carte-active': estSelectionne, 'carte-large': grandFormat }"></div>

:style — Style dynamique

<!-- Objet CSS (camelCase) -->
<div :style="{ color: couleurTexte, fontSize: taille + 'px', backgroundColor: fond }"></div>

<!-- Tableau d'objets (plusieurs sources de styles) -->
<div :style="[styleBase, styleDynamique]"></div>

<!-- Variables CSS -->
<div :style="{ '--couleur-principale': couleurPrincipale }"></div>

<!-- Exemple concret : indicateur de progression -->
<div class="barre-fond">
  <div
    class="barre-progression"
    :style="{ width: progression + '%', backgroundColor: couleur }"
  ></div>
</div>

Exemple complet

<div id="app">
  <div
    class="carte"
    :class="{ 'carte-selectionnee': selectionne, 'carte-danger': score < 5 }"
    :style="{ borderColor: couleur, transform: selectionne ? 'scale(1.03)' : 'none' }"
    @click="selectionne = !selectionne"
  >
    <h3 :style="{ color: couleur }">{{ titre }}</h3>
    <p>Score : {{ score }}</p>
  </div>
</div>

<script>
createApp({
  data() {
    return {
      titre: 'Ma carte',
      score: 7,
      couleur: '#42b883',
      selectionne: false
    };
  }
}).mount('#app');
</script>
💡 Les propriétés CSS kebab-case (font-size) doivent être écrites en camelCase (fontSize) dans les objets JS, ou entre guillemets ('font-size').

5. v-on (@) — Événements et modificateurs

v-on écoute les événements DOM. Son raccourci @ s'applique à n'importe quel événement natif ou personnalisé. Les modificateurs ajoutent un comportement supplémentaire sans polluer le code de la méthode.

Modificateurs d'événement

<!-- .prevent — evt.preventDefault() -->
<form @submit.prevent="soumettreFormulaire">...</form>
<a @click.prevent="naviguer">Lien</a>

<!-- .stop — evt.stopPropagation() -->
<button @click.stop="clicBouton">Ne propage pas</button>

<!-- .self — déclenche seulement sur l'élément lui-même -->
<div @click.self="fermerModal" class="overlay">
  <div class="modal">...</div>
</div>

<!-- .once — ne se déclenche qu'une seule fois -->
<button @click.once="accepterCGU">Accepter une fois</button>

<!-- .passive — améliore les performances pour scroll/touch -->
<div @scroll.passive="onScroll">...</div>

<!-- Chaîner les modificateurs -->
<form @submit.prevent.stop="soumettreFormulaire">...</form>

Modificateurs de touche clavier

<!-- Touches spécifiques -->
<input @keyup.enter="valider" placeholder="Appuie sur Entrée">
<input @keyup.esc="annuler">
<input @keyup.tab="passageSuivant">
<input @keydown.delete="supprimerDernier">

<!-- Touches de modification -->
<button @click.ctrl="sauvegarder">Ctrl+Clic pour sauvegarder</button>
<button @click.shift="selectionner">Shift+Clic</button>

<!-- Touches exactes -->
<input @keyup.ctrl.z.exact="annulerAction">
<input @keyup.alt.enter.exact="envoyerEtNouvelle">

Modificateurs de souris

<button @click.left="clicGauche">Clic gauche</button>
<button @click.right.prevent="menuContextuel">Clic droit</button>
<button @click.middle="ouvrirOnglet">Clic milieu</button>
✅ Les modificateurs se lisent comme du texte naturel : @submit.prevent = "quand submit, empêche le comportement par défaut".

6. v-model — Liaisons et modificateurs

v-model crée une liaison bidirectionnelle entre un champ de formulaire et une donnée Vue. Il s'adapte automatiquement au type de l'élément.

Types de champs

<div id="app">
  <!-- Input texte -->
  <input v-model="texte" type="text">

  <!-- Input nombre -->
  <input v-model.number="age" type="number">

  <!-- Checkbox -->
  <input v-model="accepte" type="checkbox"> J'accepte

  <!-- Checkbox multiple (tableau) -->
  <input v-model="couleurs" value="rouge" type="checkbox"> Rouge
  <input v-model="couleurs" value="bleu" type="checkbox"> Bleu

  <!-- Radio -->
  <input v-model="genre" value="homme" type="radio"> Homme
  <input v-model="genre" value="femme" type="radio"> Femme

  <!-- Select -->
  <select v-model="pays">
    <option value="">Choisir...</option>
    <option value="fr">France</option>
    <option value="be">Belgique</option>
  </select>

  <!-- Select multiple -->
  <select v-model="langues" multiple>
    <option v-for="l in languesDisponibles" :key="l" :value="l">{{ l }}</option>
  </select>

  <!-- Textarea -->
  <textarea v-model="message"></textarea>
</div>

Modificateurs v-model

<!-- .lazy — mise à jour au changement (blur), pas à chaque frappe -->
<input v-model.lazy="texte">

<!-- .number — convertit la valeur en nombre automatiquement -->
<input v-model.number="quantite" type="number">
<!-- Sans .number : "42" (string) → Avec .number : 42 (number) -->

<!-- .trim — enlève les espaces en début et fin -->
<input v-model.trim="email">

<!-- Combiner les modificateurs -->
<input v-model.trim.lazy="nom">

Exemple formulaire complet

<form @submit.prevent="soumettreFormulaire">
  <input v-model.trim="form.nom" placeholder="Nom">
  <input v-model.trim="form.email" type="email" placeholder="Email">
  <input v-model.number="form.age" type="number" placeholder="Âge">
  <textarea v-model.trim="form.message" placeholder="Message"></textarea>
  <select v-model="form.pays">
    <option v-for="p in pays" :key="p.code" :value="p.code">{{ p.nom }}</option>
  </select>
  <input v-model="form.cgu" type="checkbox"> J'accepte les CGU
  <button type="submit" :disabled="!form.cgu">Envoyer</button>
</form>

<!-- Aperçu en temps réel -->
<pre>{{ JSON.stringify(form, null, 2) }}</pre>
.number et .trim sont les modificateurs les plus utiles en pratique — ils évitent les surprises de typage dans tes données.
📝 Exercices V02 ▶ Mini-projet 🧠 QCM V02 ← V01 Suivant → V03