Syntaxe Fondamentale et Réactivité
Gestion du Cycle de Vie et Enregistrement des Composants
L'enregistrement des composants et la gestion du cycle de vie ont connu des évolutions majeures entre les deux versions majeures du framework.
Approche Options API (Vue 2)
L'enregistrement global ou local nécessite une déclaration explicite dans les options du composant.
// Enregistrement global
import PrimaryActionBtn from './components/PrimaryActionBtn.vue'
Vue.component('PrimaryActionBtn', PrimaryActionBtn)
// Enregistrement local avec chargement paresseux
export default {
components: {
DashboardWidget: () => import('@/components/DashboardWidget.vue')
}
}
Approche Composition API (Vue 3)
Avec le script setup, l'importation suffit. Le compilateur gère automatiquement l'enregistrement local.
<script setup>
import DashboardWidget from '@/components/DashboardWidget.vue'
</script>
Propriétés Calculées (Computed)
Les propriétés calculées permettent de dériver des états de manière réactive et mémorisée.
Vue 2 : Options API
export default {
data() {
return {
baseCost: 100,
serviceFee: 15
}
},
computed: {
finalAmount() {
return this.baseCost + this.serviceFee
},
// Getter et Setter
formattedAmount: {
get() { return `$${this.finalAmount.toFixed(2)}` },
set(val) { this.baseCost = parseFloat(val.replace('$', '')) - this.serviceFee }
}
}
}
Vue 3 : Composition API
import { ref, computed } from 'vue'
const rawUsers = ref([
{ id: 1, name: 'Alice', isActive: true },
{ id: 2, name: 'Bob', isActive: false }
])
// Syntaxe simplifiée
const activeUsers = computed(() => rawUsers.value.filter(u => u.isActive))
// Syntaxe avec getter/setter
const userCount = computed({
get: () => rawUsers.value.length,
set: (val) => { console.warn('Modification du compteur interceptée:', val) }
})
Observateurs (Watch)
Les observateurs sont utilisés pour exécuter des effets secondaires en réponse à des changements de données.
Vue 2 : Surveillance de données simples et imbriquées
export default {
data() {
return {
searchQuery: '',
userSettings: { theme: 'dark', notifications: true }
}
},
watch: {
searchQuery(newVal, oldVal) {
console.log(`Recherche mise à jour: ${oldVal} -> ${newVal}`)
},
userSettings: {
deep: true,
handler(newSettings) {
this.saveToLocalStorage(newSettings)
}
}
}
}
Vue 3 : Surveillance avec l'API de composition
import { ref, watch } from 'vue'
const config = ref({ apiUrl: 'https://api.test.com', timeout: 5000 })
// Surveillance profonde d'un objet réactif
watch(config, (newConfig, oldConfig) => {
console.log('Configuration modifiée', newConfig)
}, { deep: true, immediate: true })
// Surveillance d'une propriété spécifique
watch(() => config.value.timeout, (newTimeout) => {
console.log('Délai d'attente mis à jour:', newTimeout)
})
Manipulation du DOM et nextTick
La méthode nextTick permet de différer l'exécution d'une fonction jusqu'à ce que le DOM soit mis à jour.
Vue 3 avec Script Setup
<script setup>
import { ref, nextTick, onMounted } from 'vue'
const isVisible = ref(false)
const messageRef = ref(null)
onMounted(async () => {
isVisible.value = true
// Le DOM n'est pas encore mis à jour ici
await nextTick()
// Le DOM est maintenant synchronisé
console.log('Hauteur de l'élément:', messageRef.value.offsetHeight)
})
</script>
Directives Personnalisées
Les directives permettent d'appliquer des comportements basés sur le DOM.
Vue 3 : Focus automatique
import { createApp } from 'vue'
import App from './App.vue'
const app = createApp(App)
app.directive('auto-focus', {
mounted(el) {
el.focus()
}
})
// Utilisation dans le template : <input v-auto-focus />
Récupération des Instances et DefineExpose
Dans Vue 3, l'accès aux propriétés internes d'un composant enfant nécessite une exposition explicite.
// Composant Enfant (ChildComponent.vue)
<script setup>
import { ref } from 'vue'
const internalState = ref('Secret Data')
const resetState = () => { internalState.value = '' }
defineExpose({ internalState, resetState })
</script>
// Composant Parent
<script setup>
import { ref } from 'vue'
import ChildComponent from './ChildComponent.vue'
const childRef = ref(null)
const triggerReset = () => {
childRef.value.resetState()
}
</script>
<template>
<ChildComponent ref="childRef" />
</template>
Optimisation : Debounce avec customRef
Implémentation d'un reference personnalisée pour le debounce.
import { customRef } from 'vue'
export function useDebouncedRef(initialValue, delay = 300) {
let timeout
return customRef((track, trigger) => ({
get() {
track()
return initialValue
},
set(newValue) {
clearTimeout(timeout)
timeout = setTimeout(() => {
initialValue = newValue
trigger()
}, delay)
}
}))
}
// Utilisation
const searchInput = useDebouncedRef('', 500)
Architecture et Intégration des Modules
Gestion du Routage (Vue Router)
Configuration Vue 3 avec Vite
import { createRouter, createWebHistory } from 'vue-router'
const routes = [
{ path: '/', redirect: '/dashboard' },
{ path: '/dashboard', component: () => import('@/views/Dashboard.vue') },
{ path: '/settings', component: () => import('@/views/Settings.vue') }
]
const router = createRouter({
history: createWebHistory(),
routes
})
router.beforeEach((to, from, next) => {
const isAuthenticated = localStorage.getItem('auth_token')
if (to.meta.requiresAuth && !isAuthenticated) {
next('/login')
} else {
next()
}
})
export default router
Client HTTP (Axios)
Configuration d'une instance Axios avec intercepteurs et gestion des variables d'environnement.
import axios from 'axios'
const apiClient = axios.create({
baseURL: import.meta.env.VITE_API_BASE_URL,
timeout: 15000
})
apiClient.interceptors.request.use(config => {
const authToken = localStorage.getItem('auth_token')
if (authToken) {
config.headers.Authorization = `Bearer ${authToken}`
}
return config
})
apiClient.interceptors.response.use(
response => response.data,
error => {
if (error.response?.status === 401) {
window.location.href = '/login'
}
return Promise.reject(error)
}
)
export const fetchUserProfile = (userId) => apiClient.get(`/users/${userId}`)
Gestion d'État : Pinia
Pinia remplace avantageusement Vuex dans l'écosystème Vue 3, offrant une API plus intuitive et un support TypeScript natif.
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'
export const useShoppingCart = defineStore('cart', () => {
const items = ref([])
const totalItems = computed(() => items.value.reduce((sum, item) => sum + item.quantity, 0))
const addItem = (product) => {
const existing = items.value.find(i => i.id === product.id)
if (existing) {
existing.quantity++
} else {
items.value.push({ ...product, quantity: 1 })
}
}
return { items, totalItems, addItem }
}, {
persist: true // Nécessite le plugin pinia-plugin-persistedstate
})
// Utilisation dans un composant
import { storeToRefs } from 'pinia'
const cart = useShoppingCart()
const { items, totalItems } = storeToRefs(cart) // Conserve la réactivité
Intégration d'Element Plus
Configuration de l'importation automatique des composants et des styles via Vite.
// vite.config.js
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
export default defineConfig({
plugins: [
vue(),
AutoImport({ resolvers: [ElementPlusResolver()] }),
Components({ resolvers: [ElementPlusResolver()] })
]
})
Encapsulation de Composants : Notification Toast
Création d'une fonction globale pour afficher des notifications dynamiques.
import { createApp, ref } from 'vue'
import ToastComponent from './Toast.vue'
const notifications = ref([])
export const showToast = (options) => {
const container = document.createDocumentFragment()
const app = createApp(ToastComponent, options)
const instance = app.mount(container)
document.body.appendChild(container)
notifications.value.push(instance)
setTimeout(() => {
instance.hide()
setTimeout(() => {
app.unmount()
notifications.value = notifications.value.filter(n => n !== instance)
}, 300)
}, options.duration || 3000)
}
Configuration et Démarrage du Projet
Initialisation avec Vite
La création d'un projet Vue 3 moderne utilise l'outil officiel basé sur Vite.
# Création du projet
npm init vue@latest
# Installation des dépendances
cd mon-projet
npm install
Configuration des Alias et du Proxy (vite.config.js)
import { fileURLToPath, URL } from 'node:url'
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
export default defineConfig({
plugins: [vue()],
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
}
},
server: {
proxy: {
'/api': {
target: 'http://backend.local:8080',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, '')
}
}
}
})
Concepts Internes et Résolution de Problèmes
Principes de Réactivité
- Vue 2 : Utilise
Object.defineProperty. Limite : ne peut pas détecter l'ajout/suppression de propriétés ou les modifications par index dans les tableaux. NécessiteVue.set. - Vue 3 : Utilise
Proxy. Permet une interception profonde, supporte les structuresMapetSet, et détecte toutes les mutasions. Inconvénient : non supporté par IE11.
Computed vs Watch
- Computed : Synchrone, possède un système de cache (ne se recalcule que si ses dépendances changent). Idéal pour les dérivations de données.
- Watch : Supporte l'asynchrone, pas de cache. Idéal pour les effets secondaires (requêtes API, manipulation du DOM, stockage local).
Directives Conditionnelles et Boucles
- v-if vs v-show :
v-ifdétruit et recrée le nœud DOM (coûteux, bon pour les changements rares).v-showutilisedisplay: none(toujours rendu, bon pour les basculements fréquents). - Priorité v-for / v-if : Dans Vue 2,
v-forest prioritaire. Dans Vue 3, il est interdit de les placer sur le même nœud pour éviter les ambiguïtés de compiltaion. - Attribut key : Essentiel pour l'algorithme de Diff. Il permet à Vue d'identifier de manière unique les nœuds, optimisant ainsi le réemploi du DOM lors des réorganisations de listes.
Ordre d'Exécution du Cycle de Vie
Lors du rendu initial d'une arborescence de composants :
- Parent :
beforeCreate->created->beforeMount - Enfant :
beforeCreate->created->beforeMount->mounted - Parent :
mounted
Composants Intégrés de Vue 3
- Suspense : Permet d'afficher un contenu de secours (fallback) pendant le chargement asynchrone de composants ou de données.
- Teleport : Permet de projeter le DOM d'un composant à un autre endroit de l'arbre DOM (ex: modales attachées au
body). - Fragment : Support natif des composants multi-racines sans nécessiter de balise enveloppante.
Utilitaires de l'API de Composition
- toRef / toRefs :
toRefcrée une référence pour une propriété d'un objet réactif.toRefsconvertit toutes les propriétés d'un objet réactif en références individuelles, permettant la déstructuration sans perte de réactivité. - toRaw : Retourne l'objet brut original, sans le proxy réactif. Utile pour sérialiser ou éviter de déclencher des effets secondaires.
- markRaw : Marque un objet pour qu'il ne soit jamais converti en proxy. Utile pour les instances de bibliothèques tierces ou les données statiques volumineuses.
- customRef : Permet de créer des références avec un contrôle explicite sur le suivi des dépendances et le déclenchement des mises à jour (ex: implémentation de debounce/throttle).