Sommaire
- Introduction à Vue.js
- Créer une première application
- Interagir avec le DOM
- Données et méthodes
- Formulaires
- Les bases des composants
- Les composants en détails
- Cycle de vie des composants
1. Introduction à Vue.js
Qu'est-ce que Vue.js ?
Vue.js est un framework open-source JavaScript créé en 2014 par Evan You (ex-Google, également créateur de Vite). Il permet de créer des interfaces utilisateurs en utilisant des composants réactifs, basé sur HTML / CSS / JavaScript. Actuellement en version 3.5+.
Un framework évolutif
- Adoption progressive : peut gérer une simple partie d'une page existante, sans router ni store obligatoire.
- Montée en complexité maîtrisée : intégration optionnelle de Vue Router, Pinia et Vite.
- Aucune architecture imposée : liberté dans l'organisation du code, du petit projet à une application fullstack avec Nuxt (SSR, SSG).
Vue vs. React vs. Angular vs. Svelte
| Angular | React | Vue | Svelte | |
|---|---|---|---|---|
| Type | Framework complet | Bibliothèque UI | Framework progressif | Framework compilé |
| Apprentissage | Difficile | Moyen | Facile | Facile |
| Performance | Bonne | Bonne | Très bonne | Excellente |
| Créé par | Meta | Evan You | Rich Harris | |
| Année | 2010 | 2013 | 2014 | 2016 |
Vue.js : excellent compromis entre simplicité et puissance, syntaxe proche du HTML, montée en complexité progressive.
Composant mono-fichier (SFC)
Vue utilise des Single-File Components (.vue). Le JavaScript, le HTML et le CSS sont réunis dans un seul fichier.
<script>
// Code JavaScript ici.
</script>
<template>
<!-- Code HTML ici. -->
</template>
<style>
/* Code CSS ici. */
</style>
Options API vs. Composition API
Vue propose deux styles d'API :
Options API (historique, Vue 2) :
<script>
export default {
data() { return { count: 0 } },
methods: {
increment() { this.count++ }
},
mounted() { console.log(this.count) }
}
</script>
Composition API (recommandée, Vue 3) :
<script setup>
import { ref, onMounted } from 'vue'
const count = ref(0)
function increment() { count.value++ }
onMounted(() => console.log(count.value))
</script>
| Critères | Options API | Composition API |
|---|---|---|
| Organisation | Par sections (data, methods…) | Par fonctionnalités |
| Réutilisabilité | Difficile sans mixins | Facile via fonctions/hooks |
| Complexité | Simple pour composants basiques | Meilleure pour composants complexes |
| Performance | Vue 2 | Optimisée pour Vue 3 |
💡 Dans ce cours, on utilisera uniquement la Composition API.
2. Créer une première application
Installation
Prérequis : avoir Node.js et npm installés.
npm create vue@latest
Lors de l'installation, sélectionner : TypeScript, ESLint, Prettier (au minimum).
cd mon-projet
npm install && npm run dev
Architecture d'un projet Vue
/public → assets statiques
/src
main.ts → fichier d'entrée, initialise Vue.js
App.vue → composant principal
/components → composants réutilisables
/pages → pages de l'application
💡 Par convention, les fichiers de composants sont nommés en PascalCase (ex:
MonComposant.vue).
3. Interagir avec le DOM
Interpolation de données
Affichage dynamique avec la syntaxe Moustache {{ }} :
<script setup>
const message = "Hello World";
</script>
<template>
<p>{{ message }}</p>
<p>{{ 2 + 2 }}</p> <!-- Affiche 4 -->
</template>
Les directives
Une directive est un attribut spécial commençant par v- qui lie du JavaScript au DOM.
| Directive | Rôle |
|---|---|
v-text |
Afficher du texte dynamique |
v-html |
Interpréter du HTML (⚠️ risque XSS) |
v-bind / : |
Lier des attributs HTML dynamiquement |
v-model |
Liaison bidirectionnelle avec un champ |
v-if / v-else-if / v-else |
Rendu conditionnel |
v-show |
Afficher/masquer (via display: none) |
v-for |
Boucles sur tableaux et objets |
v-on / @ |
Écouter des événements |
v-bind et son raccourci
<img v-bind:src="imageUrl" v-bind:alt="imageAlt"/>
<!-- Équivalent (recommandé) -->
<img :src="imageUrl" :alt="imageAlt"/>
v-model
<input type="text" v-model="nom" placeholder="Votre nom"/>
<p>Bonjour, {{ nom }} !</p>
v-if / v-else
<p v-if="nom">Bonjour {{ nom }} !</p>
<p v-else>Bonjour inconnu !</p>
💡 Utilisez
v-showsi vous alternez fréquemment,v-ifsi le changement est rare.
v-for
<li v-for="(person, index) in people" :key="person.name">
{{ index }} - {{ person.name }} ({{ person.age }} ans)
</li>
Gestion des événements
<button @click="direBonjour('Alice')">Dire bonjour</button>
Modificateurs d'événements
| Modificateur | Description |
|---|---|
.stop |
Arrête la propagation |
.prevent |
Empêche le comportement par défaut |
.once |
L'événement ne se déclenche qu'une fois |
.self |
Uniquement si l'élément lui-même est la cible |
<form @submit.prevent="submitForm">...</form>
<button @click.once="sayHello">Dire bonjour</button>
<input @keyup.enter="submit"/>
4. Données et méthodes
La réactivité
Vue crée un système réactif basé sur des Proxys JavaScript. Toute modification d'une variable réactive déclenche une mise à jour automatique de la vue.
reactive() — pour les objets
<script setup>
import { reactive } from 'vue'
const user = reactive({ name: "Alice", age: 25 })
user.age++ // Vue détecte le changement !
</script>
⚠️ Limitations : ne fonctionne pas avec les primitives, impossible de déstructurer sans perdre la réactivité.
ref() — pour tout type de valeur
<script setup>
import { ref } from 'vue'
const name = ref('Alice')
const age = ref(25)
age.value++ // Utiliser .value dans le script
</script>
<template>
<p>{{ name }} a {{ age }} ans</p>
<!-- Dans le template, pas besoin de .value -->
</template>
reactive() |
ref() |
|
|---|---|---|
| Type | Objets uniquement | Primitifs & objets |
| Accès | Direct | .value obligatoire (dans le script) |
| Usage | Structures complexes | Valeurs simples |
💡 On peut tout à fait n'utiliser que
ref()pour ne plus avoir à choisir.
Propriétés calculées (computed)
<script setup>
import { ref, computed } from 'vue'
const prixHT = ref(100)
const tva = 0.2
const prixTTC = computed(() => prixHT.value * (1 + tva))
</script>
Les propriétés computed sont mises en cache et ne se recalculent que si leurs dépendances changent. Préférer computed aux méthodes pour calculer des valeurs dérivées.
Observateurs (watch)
<script setup>
import { ref, watch } from 'vue'
const compteur = ref(0)
watch(compteur, (nouveau, ancien) => {
console.log(`Compteur changé de ${ancien} à ${nouveau}`)
})
</script>
Pour surveiller un objet en profondeur :
watch(utilisateur, (nouveau) => { ... }, { deep: true })
watchEffect s'exécute immédiatement et réagit automatiquement :
watchEffect(() => {
console.log(`Message actuel : ${message.value}`)
})
computed |
watch |
methods |
|
|---|---|---|---|
| Mise en cache | ✅ | ❌ | ❌ |
| Usage | Valeurs dérivées | Actions sur changement | Exécution d'actions |
| Async | ❌ | ✅ | ✅ |
5. Formulaires
Champs avec v-model
<!-- Input texte -->
<input type="text" v-model="message"/>
<!-- Textarea -->
<textarea v-model="message"></textarea>
<!-- Checkbox (booléen) -->
<input type="checkbox" v-model="checked"/>
<!-- Radio -->
<input type="radio" value="Choix n°1" v-model="choice"/>
<input type="radio" value="Choix n°2" v-model="choice"/>
<!-- Select -->
<select v-model="choice">
<option v-for="option in options" :key="option.value" :value="option.value">
{{ option.text }}
</option>
</select>
Modificateurs de v-model
| Modificateur | Effet |
|---|---|
.lazy |
Synchronise sur l'événement change (pas input) |
.number |
Convertit la valeur en nombre |
.trim |
Supprime les espaces avant/après |
<input type="email" v-model.trim="email"/>
<input type="number" v-model.number="age"/>
6. Les bases des composants
Introduction
Les composants sont les blocs de construction d'une application Vue. Chaque composant représente une partie réutilisable de l'interface (ex: <Header/>, <Button/>, <FormContact/>).
Utiliser un composant
<script setup>
import MonComposant from './MonComposant.vue'
</script>
<template>
<MonComposant/>
</template>
Props — passer des données parent → enfant
<!-- Parent.vue -->
<Enfant message="Hello !"/>
<!-- Enfant.vue -->
<script setup>
defineProps(['message'])
</script>
<template>
<p>{{ message }}</p>
</template>
⚠️ Il est interdit de modifier une prop directement depuis le composant enfant.
Emit — envoyer un événement enfant → parent
<!-- Enfant.vue -->
<script setup>
const emit = defineEmits(['increment'])
</script>
<template>
<button @click="emit('increment')">+1</button>
</template>
<!-- Parent.vue -->
<Enfant @increment="compteur++"/>
Slots — insérer du contenu dans un composant
<!-- MaCard.vue -->
<template>
<div class="card">
<slot/>
</div>
</template>
<!-- Parent.vue -->
<MaCard>
<p>Contenu inséré dans le slot</p>
</MaCard>
Organisation recommandée
/src
/components → composants UI réutilisables
/pages → pages principales
/layouts → templates de mise en page
7. Les composants en détails
Props typées et validées
<script setup>
defineProps({
titre: String,
compteur: {
type: Number,
required: true,
default: 0
},
type: {
type: String,
required: true,
validator(value) {
return ['success', 'warning', 'danger'].includes(value)
}
}
})
</script>
Équivalent TypeScript :
<script setup lang="ts">
const props = defineProps<{
message: string
type: 'success' | 'warning' | 'danger'
}>()
</script>
v-model sur un composant personnalisé
<!-- Parent.vue -->
<MonComposant v-model="texte"/>
<!-- Enfant.vue (Vue >= 3.4) -->
<script setup>
const model = defineModel()
</script>
<template>
<input type="text" v-model="model"/>
</template>
Attributs implicites ($attrs)
Par défaut, les attributs passés à un composant sont appliqués à son élément racine. Pour désactiver ce comportement et les appliquer manuellement :
<script setup>
defineOptions({ inheritAttrs: false })
</script>
<template>
<label>Mon input</label>
<input type="text" v-bind="$attrs"/>
</template>
Provide / Inject
Partager des données entre un composant ancêtre et ses descendants sans passer par les props à chaque niveau.
<!-- Fournisseur (parent ou layout) -->
<script setup>
import { provide, ref } from 'vue'
const theme = ref('dark')
provide('theme', theme)
</script>
<!-- Consommateur (n'importe quel descendant) -->
<script setup>
import { inject } from 'vue'
const theme = inject('theme')
</script>
Composants asynchrones
<script setup>
import { defineAsyncComponent } from 'vue'
const AsyncComponent = defineAsyncComponent(() => import('./MonComposant.vue'))
</script>
8. Cycle de vie des composants
Les phases
Un composant Vue passe par 4 phases :
1. Création
beforeCreate: composant initialisé, données pas encore disponibles.created: données réactives, props, computed disponibles. DOM inexistant.
2. Montage
beforeMount: template compilé, DOM pas encore inséré.mounted: composant inséré dans le DOM. ✅ Accès au DOM possible.
3. Mise à jour
beforeUpdate: une donnée change, DOM pas encore mis à jour.updated: DOM mis à jour.
4. Destruction
beforeUnmount: composant sur le point d'être retiré. Idéal pour nettoyer (timers, listeners).unmounted: composant totalement détruit.
Utilisation des hooks
<script setup>
import { ref, onMounted, onBeforeUnmount } from 'vue'
const count = ref(0)
let interval
onMounted(() => {
interval = setInterval(() => { count.value++ }, 1000)
})
onBeforeUnmount(() => {
clearInterval(interval) // Nettoyage !
})
</script>
Écouter le redimensionnement de la fenêtre
<script setup>
import { onMounted, onBeforeUnmount } from 'vue'
function handleResize() {
console.log('Largeur :', window.innerWidth)
}
onMounted(() => window.addEventListener('resize', handleResize))
onBeforeUnmount(() => window.removeEventListener('resize', handleResize))
</script>