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