Directives
đ Rendu conditionnel
v-if
Rend l'élément si la condition est vraie. Détruit/recrée le DOM.
v-else-if
Condition sinon-si, doit suivre v-if.
v-else
Sinon, doit suivre v-if ou v-else-if.
v-show
Toggle display:none, le DOM reste. Préférer pour basculements fréquents.
<div v-if="role === 'admin'">Admin</div>
<div v-else-if="role === 'user'">User</div>
<div v-else>Invité</div>
<p v-show="isVisible">Visible</p>
đ Listes
v-for
Itérer sur un tableau, objet ou range. Toujours utiliser :key.
// Tableau
<li v-for="item in items" :key="item.id">
{{ item.name }}
</li>
// Avec index
<li v-for="(item, i) in items" :key="item.id">
{{ i + 1 }}. {{ item.name }}
</li>
// Objet
<li v-for="(val, key) in obj" :key="key">
{{ key }}: {{ val }}
</li>
// Range
<span v-for="n in 5" :key="n">{{ n }}</span>
đ Binding dynamique
v-bind / :
Lie un attribut Ă une expression JS.
:class
Binding conditionnel de classes.
:style
Binding inline de styles.
// Attribut
<img :src="imageUrl" :alt="title" />
// :class objet
<div :class="{ active: isActive, error: hasError }">
// :class tableau
<div :class="[baseClass, isActive ? 'on' : '']">
// :style objet
<p :style="{ color: textColor, fontSize: size + 'px' }">
đĄ ĂvĂ©nements
v-on / @
Ăcouter un Ă©vĂ©nement DOM.
// Clic
<button @click="handleClick">Click</button>
// Avec modificateurs
<form @submit.prevent="onSubmit">
<div @click.stop="fn"> // stopPropagation
<a @click.once="fn"> // une seule fois
<input @keyup.enter="fn">
// Inline avec $event
<button @click="handleClick($event)">
đ v-model
v-model
Binding bidirectionnel sur les inputs.
// Text
<input v-model="text" />
// Modificateurs
<input v-model.trim="name" />
<input v-model.number="age" type="number" />
<input v-model.lazy="bio" />
// Checkbox
<input type="checkbox" v-model="checked" />
// Select
<select v-model="selected">
<option value="a">Option A</option>
</select>
⥠Performance
v-memo
Mémoïse un sous-arbre, ne re-rend que si les dépendances changent.
v-once
Rendu unique, jamais mis Ă jour.
v-pre
Saute la compilation de ce sous-arbre (contenu brut).
// v-memo avec dépendances
<li v-memo="[item.id === selectedId]"
v-for="item in list" :key="item.id">
// v-once
<h1 v-once>{{ title }}</h1>
// v-pre (affiche les moustaches bruts)
<span v-pre>{{ non compilé }}</span>
Composition API
đŽ RĂ©activitĂ© de base
ref()
Valeur réactive simple (primitives ou objets). AccÚs via .value.
reactive()
Objet réactif profond. Pas de .value.
toRefs()
Déstructure un reactive() en refs sans perdre la réactivité.
import { ref, reactive, toRefs } from 'vue'
const count = ref(0)
count.value++ // .value obligatoire en JS
// count dans le template : pas de .value
const state = reactive({ name: 'Alice', age: 25 })
state.age++ // direct, pas de .value
const { name, age } = toRefs(state)
đ§ź Computed & Watch
computed()
Valeur dérivée mémoïsée. Recalculée seulement si dépendances changent.
watch()
Observer une source explicite. AccĂšs aux old/new values.
watchEffect()
Tracking auto des dépendances. Exécuté immédiatement.
const doubled = computed(() => count.value * 2)
watch(count, (newVal, oldVal) => {
// déclenché seulement si count change
}, { immediate: true, deep: false })
watchEffect(() => {
console.log(count.value) // track auto
})
// Plusieurs sources
watch([a, b], ([newA, newB]) => { /* ... */ })
âïž Lifecycle hooks
import {
onBeforeMount, onMounted,
onBeforeUpdate, onUpdated,
onBeforeUnmount, onUnmounted,
onActivated, onDeactivated
} from 'vue'
onMounted(() => {
// DOM disponible, fetch, setInterval
})
onUnmounted(() => {
// Nettoyage : clearInterval, removeEventListener
})
đŻ Utilitaires
nextTick()
Promise aprĂšs mise Ă jour du DOM.
shallowRef()
Réactivité superficielle uniquement.
markRaw()
EmpĂȘche Vue de rendre un objet rĂ©actif.
triggerRef()
Force la mise Ă jour d'une shallowRef.
import { nextTick, shallowRef, markRaw, triggerRef } from 'vue'
await nextTick() // DOM mis Ă jour
const big = shallowRef({ data: [] })
big.value.data.push(1)
triggerRef(big) // force re-rendu
const chart = markRaw(new Chart())
Composants
đ„ Props
// Enfant â defineProps
const props = defineProps({
title: { type: String, required: true },
count: { type: Number, default: 0 },
items: { type: Array, default: () => [] },
callback: { type: Function }
})
// Parent â passage des props
<MyComp :title="pageTitle" :count="42" />
đ€ Emits
// Enfant
const emit = defineEmits(['update', 'submit'])
function handleClick() {
emit('update', newValue)
}
// Parent
<MyComp @update="handleUpdate" />
// v-model sur composant
// Enfant :
const props = defineProps(['modelValue'])
const emit = defineEmits(['update:modelValue'])
// Parent :
<MyInput v-model="name" />
đȘ Slots
// Slot par défaut
<slot />
// Slot nommé
<slot name="header">Fallback</slot>
// Utilisation (parent)
<MyCard>
<template #header>Titre</template>
<p>Contenu par défaut</p>
</MyCard>
// Scoped slot
<slot :item="item" />
// Parent :
<template #default="{ item }">
đ Provide / Inject
// AncĂȘtre â provide
import { provide, ref } from 'vue'
const theme = ref('dark')
provide('theme', theme)
provide('toggleTheme', () => {
theme.value = theme.value === 'dark' ? 'light' : 'dark'
})
// Descendant â inject
import { inject } from 'vue'
const theme = inject('theme')
const toggle = inject('toggleTheme')
đȘ KeepAlive & Teleport
// KeepAlive â conserve l'Ă©tat
<KeepAlive :max="5" include="Tab,Panel">
<component :is="currentTab" />
</KeepAlive>
// Hooks KeepAlive
onActivated(() => { /* sorti du cache */ })
onDeactivated(() => { /* mis en cache */ })
// Teleport â rendu ailleurs dans le DOM
<Teleport to="body">
<div v-if="showModal" class="modal">...</div>
</Teleport>
Vue Router
đïž Configuration
import { createRouter, createWebHashHistory } from 'vue-router'
const router = createRouter({
history: createWebHashHistory(),
routes: [
{ path: '/', component: Home },
{ path: '/about', component: About },
{ path: '/user/:id', component: User },
{ path: '/:path(.*)', component: NotFound }
]
})
app.use(router)
đ Navigation
// Template
<RouterLink to="/">Accueil</RouterLink>
<RouterLink :to="{ name: 'user', params: { id: 1 } }">
<RouterView />
// Composition API
import { useRoute, useRouter } from 'vue-router'
const route = useRoute()
const router = useRouter()
route.params.id // paramĂštre :id
route.query.search // ?search=...
route.path // chemin actuel
router.push('/about')
router.replace('/home')
router.go(-1)
đĄïž Navigation Guards
// Global avant chaque navigation
router.beforeEach((to, from) => {
if (to.meta.requiresAuth && !isLoggedIn) {
return { name: 'login' }
}
})
// Par route
{ path: '/admin', component: Admin,
beforeEnter: (to, from) => { /* guard */ } }
// Dans un composant
onBeforeRouteLeave((to, from) => {
if (hasUnsavedChanges) return false
})
Pinia
đ DĂ©finir un store
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'
export const useCounterStore = defineStore('counter', () => {
// state
const count = ref(0)
// getters
const doubled = computed(() => count.value * 2)
// actions
function increment() { count.value++ }
async function fetchData() { /* await fetch */ }
return { count, doubled, increment, fetchData }
})
đ Utiliser un store
import { storeToRefs } from 'pinia'
import { useCounterStore } from './stores/counter'
const store = useCounterStore()
// â DĂ©structuration perd la rĂ©activitĂ©
const { count } = store
// â storeToRefs prĂ©serve la rĂ©activitĂ©
const { count, doubled } = storeToRefs(store)
// Actions (pas besoin de storeToRefs)
store.increment()
store.fetchData()
// Mutation directe (Pinia autorise)
store.count = 42
store.$patch({ count: 5 })
âïž Installation
// main.js
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'
const app = createApp(App)
app.use(createPinia())
app.mount('#app')
// En CDN
const { createPinia } = Pinia
app.use(createPinia())
Vue vs React â Comparatif
| Concept | React | Vue 3 |
|---|---|---|
| Ătat local | useState(0) |
ref(0) / reactive({}) |
| Effet au montage | useEffect(() => {}, []) |
onMounted(() => {}) |
| Effet au démontage | useEffect(() => { return () => {} }, []) |
onUnmounted(() => {}) |
| Valeur dérivée | useMemo(() => val, [dep]) |
computed(() => val) |
| Observer un changement | useEffect(() => {}, [dep]) |
watch(dep, callback) |
| Context global | createContext / useContext |
provide() / inject() |
| Mémo composant | React.memo(Component) |
v-memo="[deps]" |
| Ref DOM | useRef(null) |
ref(null) + ref="el" |
| Logique réutilisable | Custom hooks (useXxx) |
Composables (useXxx) |
| Gestion d'état globale | Redux / Zustand / Jotai | Pinia |
| Routing | React Router | Vue Router |
| Formulaires | onChange + value (contrÎlé) |
v-model (bidirectionnel) |
âïž React â đ Vue : Migration rapide
// React â â Vue â
useState(0) â ref(0)
setState(val) â myRef.value = val
useMemo(fn, [dep]) â computed(fn)
useCallback(fn,[]) â // pas besoin, les mĂ©thodes sont stables
useRef(null) â ref(null)
useEffect(fn, []) â onMounted(fn)
useContext(ctx) â inject('key')
React.memo() â v-memo ou computed
{condition && JSX} â v-if="condition"
items.map(JSX) â v-for="item in items"
className â :class
onClick â @click
onChange â @input ou v-model
đ DiffĂ©rences clĂ©s Ă retenir
.value
En Vue, les refs nécessitent .value en JS, mais pas dans le template.
Template HTML
Vue utilise des templates HTML enrichis (pas JSX). Plus lisible pour les débutants.
Réactivité
Vue détecte automatiquement les changements. React nécessite setState explicitement.
v-model
Vue a v-model natif pour les formulaires. React utilise des inputs contrÎlés manuellement.
computed
Les computed Vue sont mémoïsés automatiquement. useMemo en React nécessite un tableau de dépendances.
Liens utiles
đ Vue.js Docs
vuejs.org
Documentation officielle Vue 3. Excellent guide de démarrage.
đ Pinia
pinia.vuejs.org
State management officiel pour Vue 3.
đșïž Vue Router
router.vuejs.org
Routeur officiel SPA pour Vue 3.
đ§ VueUse
vueuse.org
BibliothĂšque de composables utilitaires prĂȘts Ă l'emploi.
⥠Vite
vitejs.dev
Build tool ultra-rapide recommandé avec Vue 3.
đ Vue School
vueschool.io
Cours vidéo officiels Vue 3.
â¶ Vue Playground
play.vuejs.org
Bac Ă sable officiel Vue 3 en ligne.
đ Vue DevTools
devtools.vuejs.org
Extension navigateur pour déboguer les apps Vue.