Comment passer des paramètres URL avec Vue Router et props

Maîtrisez les routes dynamiques avec Vue Router. Passez des paramètres URL, utilisez props: true et naviguez programmatiquement dans vos applications Vue.js.

Mahmoud DEVO
Mahmoud DEVO
December 27, 2025 9 min read
Comment passer des paramètres URL avec Vue Router et props

Comment fonctionne l’accès aux URL dynamiques dans Vue Router ?

Introduction au routing dans les SPA

Les Single Page Applications (SPA) ont révolutionné la façon dont nous construisons des applications web. Contrairement aux applications traditionnelles où chaque navigation déclenche un rechargement complet de la page, les SPA chargent une seule page HTML et mettent à jour dynamiquement le contenu en fonction des interactions utilisateur.

Vue Router est la bibliothèque officielle de routing pour Vue.js. Elle permet de :

  • Mapper des URLs à des composants Vue
  • Gérer l’historique de navigation du navigateur
  • Passer des paramètres entre les pages
  • Protéger des routes avec des guards de navigation
  • Créer des architectures d’URL complexes avec des routes imbriquées

Dans ce guide complet, nous allons explorer en profondeur les routes dynamiques avec Vue Router, en utilisant un exemple concret d’application e-commerce.

Pourquoi les routes dynamiques sont essentielles ?

Imaginez une boutique en ligne avec des milliers de produits. Créer une route statique pour chaque produit serait impossible et non maintenable :

// Mauvaise approche : routes statiques
routes: [
  { path: '/products/1', component: Product1 },
  { path: '/products/2', component: Product2 },
  { path: '/products/3', component: Product3 },
  // ... des milliers de routes
]

Les routes dynamiques résolvent ce problème en utilisant des segments de paramètres :

// Bonne approche : route dynamique
routes: [
  { path: '/products/:id', component: ProductDetail }
]

Configuration des routes dynamiques

Installation et configuration de base

Avant de commencer, assurez-vous d’avoir Vue Router installé dans votre projet :

npm install vue-router@4

Créez ensuite votre fichier de configuration de routes :

// src/router/index.ts
import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router'
import Home from '@/views/Home.vue'
import ProductList from '@/views/ProductList.vue'
import ProductDetail from '@/views/ProductDetail.vue'
import Cart from '@/views/Cart.vue'

const routes: RouteRecordRaw[] = [
  {
    path: '/',
    name: 'home',
    component: Home
  },
  {
    path: '/products',
    name: 'products',
    component: ProductList
  },
  {
    path: '/products/:id',
    name: 'product-detail',
    component: ProductDetail,
    props: true
  },
  {
    path: '/cart',
    name: 'cart',
    component: Cart
  }
]

const router = createRouter({
  history: createWebHistory(),
  routes
})

export default router

Syntaxe des paramètres dynamiques

Vue Router utilise la syntaxe :paramName pour définir des segments dynamiques :

// Paramètre simple
{ path: '/users/:userId', component: UserProfile }

// Plusieurs paramètres
{ path: '/users/:userId/posts/:postId', component: UserPost }

// Paramètre avec expression régulière personnalisée
{ path: '/products/:id(\\d+)', component: ProductDetail }  // Seulement des chiffres

// Paramètre répétable (zero ou plus)
{ path: '/files/:pathMatch(.*)*', component: FileExplorer }

// Paramètre répétable (un ou plus)
{ path: '/categories/:categories+', component: CategoryView }

Paramètres multiples et optionnels

Routes avec plusieurs paramètres

Dans une application e-commerce, vous pourriez avoir besoin de routes avec plusieurs paramètres :

// src/router/index.ts
const routes: RouteRecordRaw[] = [
  // Produit dans une catégorie
  {
    path: '/category/:categorySlug/product/:productId',
    name: 'category-product',
    component: ProductDetail,
    props: true
  },

  // Avis d'un utilisateur sur un produit
  {
    path: '/products/:productId/reviews/:reviewId',
    name: 'product-review',
    component: ReviewDetail,
    props: true
  },

  // Commande d'un utilisateur
  {
    path: '/users/:userId/orders/:orderId',
    name: 'user-order',
    component: OrderDetail,
    props: true
  }
]

Paramètres optionnels

Pour rendre un paramètre optionnel, ajoutez un ? après le nom du paramètre :

const routes: RouteRecordRaw[] = [
  // La page affiche soit tous les produits, soit ceux d'une catégorie
  {
    path: '/products/:category?',
    name: 'products',
    component: ProductList,
    props: true
  },

  // Pagination optionnelle
  {
    path: '/blog/:page?',
    name: 'blog',
    component: BlogList,
    props: route => ({
      page: parseInt(route.params.page as string) || 1
    })
  }
]

Exemple complet avec composant

<!-- src/views/ProductList.vue -->
<template>
  <div class="product-list">
    <h1 v-if="category">
      Produits dans la catégorie : {{ categoryName }}
    </h1>
    <h1 v-else>Tous les produits</h1>

    <div class="filters">
      <select v-model="selectedCategory" @change="filterByCategory">
        <option value="">Toutes les catégories</option>
        <option
          v-for="cat in categories"
          :key="cat.slug"
          :value="cat.slug"
        >
          {{ cat.name }}
        </option>
      </select>
    </div>

    <div class="products-grid">
      <ProductCard
        v-for="product in filteredProducts"
        :key="product.id"
        :product="product"
      />
    </div>
  </div>
</template>

<script setup lang="ts">
import { computed, ref, watch } from 'vue'
import { useRouter, useRoute } from 'vue-router'
import { useProductStore } from '@/stores/products'
import ProductCard from '@/components/ProductCard.vue'

// Props reçues via la route
const props = defineProps<{
  category?: string
}>()

const router = useRouter()
const route = useRoute()
const productStore = useProductStore()

const selectedCategory = ref(props.category || '')
const categories = computed(() => productStore.categories)

const categoryName = computed(() => {
  const cat = categories.value.find(c => c.slug === props.category)
  return cat?.name || props.category
})

const filteredProducts = computed(() => {
  if (props.category) {
    return productStore.getProductsByCategory(props.category)
  }
  return productStore.allProducts
})

function filterByCategory() {
  if (selectedCategory.value) {
    router.push({ name: 'products', params: { category: selectedCategory.value } })
  } else {
    router.push({ name: 'products' })
  }
}

// Synchroniser le select avec l'URL
watch(() => props.category, (newCategory) => {
  selectedCategory.value = newCategory || ''
})
</script>

Query params vs Route params

Différences fondamentales

AspectRoute ParamsQuery Params
Syntaxe URL/products/123/products?id=123
DéfinitionDans la config des routesN’importe où
ObligatoireOui (sauf avec ?)Non
Usage typiqueIdentifier une ressourceFiltrer, trier, paginer
SEOMeilleur pour les ressourcesMoins important
Accèsroute.params.idroute.query.id

Quand utiliser quoi ?

Route params - Pour identifier une ressource unique :

// Bon usage des route params
/products/123          // Un produit spécifique
/users/john-doe        // Un utilisateur spécifique
/categories/electronics // Une catégorie spécifique

Query params - Pour filtrer, trier ou paginer :

// Bon usage des query params
/products?category=electronics&sort=price&order=asc
/products?page=2&limit=20
/search?q=laptop&brand=apple&minPrice=500

Exemple combiné

// Configuration de la route
{
  path: '/products/:category?',
  name: 'products',
  component: ProductList,
  props: route => ({
    category: route.params.category,
    page: parseInt(route.query.page as string) || 1,
    sort: route.query.sort || 'name',
    order: route.query.order || 'asc',
    search: route.query.search || ''
  })
}
<!-- Composant utilisant les deux -->
<template>
  <div>
    <h1>{{ category ? `Catégorie: ${category}` : 'Tous les produits' }}</h1>

    <input
      v-model="searchQuery"
      @input="updateFilters"
      placeholder="Rechercher..."
    />

    <select v-model="sortBy" @change="updateFilters">
      <option value="name">Nom</option>
      <option value="price">Prix</option>
      <option value="date">Date</option>
    </select>

    <Pagination
      :current-page="page"
      :total-pages="totalPages"
      @page-change="changePage"
    />
  </div>
</template>

<script setup lang="ts">
import { ref, watch } from 'vue'
import { useRouter, useRoute } from 'vue-router'

const props = defineProps<{
  category?: string
  page: number
  sort: string
  order: string
  search: string
}>()

const router = useRouter()
const route = useRoute()

const searchQuery = ref(props.search)
const sortBy = ref(props.sort)

function updateFilters() {
  router.push({
    name: 'products',
    params: { category: props.category },
    query: {
      ...route.query,
      search: searchQuery.value || undefined,
      sort: sortBy.value,
      page: 1 // Revenir à la page 1 quand on filtre
    }
  })
}

function changePage(newPage: number) {
  router.push({
    name: 'products',
    params: { category: props.category },
    query: {
      ...route.query,
      page: newPage
    }
  })
}
</script>

Props: true pour découpler les composants

Le problème du couplage

Sans props: true, vos composants sont fortement couplés à Vue Router :

<!-- Couplé à Vue Router - difficile à tester -->
<script setup lang="ts">
import { useRoute } from 'vue-router'

const route = useRoute()
const productId = route.params.id  // Dépendance directe
</script>

La solution avec props: true

Avec props: true, le composant reçoit les paramètres comme des props normales :

// Configuration de la route
{
  path: '/products/:id',
  name: 'product-detail',
  component: ProductDetail,
  props: true  // Les params deviennent des props
}
<!-- Découplé - facilement testable -->
<script setup lang="ts">
// Le composant ne sait même pas qu'il est utilisé avec Vue Router
const props = defineProps<{
  id: string
}>()

// Utilisation simple
console.log(props.id)
</script>

Modes de props avancés

Vue Router offre trois modes pour les props :

const routes: RouteRecordRaw[] = [
  // Mode booléen : route.params devient props
  {
    path: '/products/:id',
    component: ProductDetail,
    props: true
  },

  // Mode objet : props statiques
  {
    path: '/promotions',
    component: ProductList,
    props: { featured: true, limit: 10 }
  },

  // Mode fonction : transformation personnalisée
  {
    path: '/products/:id',
    component: ProductDetail,
    props: route => ({
      id: parseInt(route.params.id as string),
      preview: route.query.preview === 'true',
      referrer: route.query.ref || 'direct'
    })
  }
]

Exemple complet avec typage TypeScript

// types/product.ts
export interface ProductDetailProps {
  id: number
  preview: boolean
  referrer: string
}

// router/index.ts
{
  path: '/products/:id',
  name: 'product-detail',
  component: () => import('@/views/ProductDetail.vue'),
  props: (route): ProductDetailProps => ({
    id: parseInt(route.params.id as string),
    preview: route.query.preview === 'true',
    referrer: (route.query.ref as string) || 'direct'
  })
}
<!-- views/ProductDetail.vue -->
<template>
  <div class="product-detail" :class="{ 'preview-mode': preview }">
    <div v-if="preview" class="preview-banner">
      Mode aperçu - Ce produit n'est pas encore publié
    </div>

    <div v-if="loading" class="loading">Chargement...</div>

    <template v-else-if="product">
      <img :src="product.image" :alt="product.name" />
      <h1>{{ product.name }}</h1>
      <p class="price">{{ formatPrice(product.price) }}</p>
      <p class="description">{{ product.description }}</p>

      <button @click="addToCart" :disabled="preview">
        {{ preview ? 'Indisponible en aperçu' : 'Ajouter au panier' }}
      </button>
    </template>

    <div v-else class="not-found">
      Produit non trouvé
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref, onMounted, watch } from 'vue'
import { useProductStore } from '@/stores/products'
import { useCartStore } from '@/stores/cart'
import type { Product } from '@/types/product'

const props = defineProps<{
  id: number
  preview: boolean
  referrer: string
}>()

const productStore = useProductStore()
const cartStore = useCartStore()

const product = ref<Product | null>(null)
const loading = ref(true)

async function loadProduct() {
  loading.value = true
  try {
    product.value = await productStore.fetchProduct(props.id)

    // Tracking analytics
    if (props.referrer !== 'direct') {
      trackReferral(props.referrer, props.id)
    }
  } catch (error) {
    console.error('Erreur lors du chargement du produit:', error)
    product.value = null
  } finally {
    loading.value = false
  }
}

function addToCart() {
  if (product.value && !props.preview) {
    cartStore.addItem(product.value)
  }
}

function formatPrice(price: number): string {
  return new Intl.NumberFormat('fr-FR', {
    style: 'currency',
    currency: 'EUR'
  }).format(price)
}

function trackReferral(source: string, productId: number) {
  // Envoi des données analytics
  console.log(`Visite depuis ${source} pour le produit ${productId}`)
}

onMounted(loadProduct)

// Recharger si l'ID change (navigation entre produits)
watch(() => props.id, loadProduct)
</script>

Vue Router offre plusieurs méthodes pour naviguer programmatiquement dans votre application.

Méthodes de navigation

router.push()

Ajoute une nouvelle entrée dans l’historique :

import { useRouter } from 'vue-router'

const router = useRouter()

// Navigation simple par chemin
router.push('/products')

// Navigation par nom de route
router.push({ name: 'products' })

// Avec paramètres
router.push({ name: 'product-detail', params: { id: '123' } })

// Avec query params
router.push({
  name: 'products',
  query: { category: 'electronics', sort: 'price' }
})

// Combinaison complète
router.push({
  name: 'product-detail',
  params: { id: '123' },
  query: { preview: 'true' },
  hash: '#reviews'
})

router.replace()

Remplace l’entrée actuelle sans ajouter à l’historique :

// Utile après une action qui ne doit pas être "retourable"
async function submitOrder() {
  const order = await orderStore.createOrder()

  // L'utilisateur ne peut pas revenir au formulaire avec "retour"
  router.replace({
    name: 'order-confirmation',
    params: { orderId: order.id }
  })
}

// Utile pour les redirections
function handleLogin() {
  const redirectTo = route.query.redirect as string || '/'
  router.replace(redirectTo)
}

router.go()

Navigation dans l’historique :

// Équivalent à cliquer sur le bouton "Retour" du navigateur
router.go(-1)

// Avancer d'une page
router.go(1)

// Reculer de 3 pages
router.go(-3)

// Rafraîchir la page actuelle (rarement utilisé)
router.go(0)

Tableau comparatif des méthodes de navigation

MéthodeHistoriqueUsage typiqueExemple
push()AjouteNavigation standardClic sur un lien
replace()RemplaceRedirection après actionAprès login
go(n)NavigateNavigation historiqueBoutons retour/avancer
back()ReculeRaccourci pour go(-1)Bouton retour
forward()AvanceRaccourci pour go(1)Bouton avancer

Exemple complet : Flux d’achat e-commerce

<!-- views/Checkout.vue -->
<template>
  <div class="checkout">
    <div class="steps">
      <span :class="{ active: step === 1 }">1. Panier</span>
      <span :class="{ active: step === 2 }">2. Livraison</span>
      <span :class="{ active: step === 3 }">3. Paiement</span>
      <span :class="{ active: step === 4 }">4. Confirmation</span>
    </div>

    <component :is="currentStepComponent" @next="nextStep" @back="previousStep" />
  </div>
</template>

<script setup lang="ts">
import { computed } from 'vue'
import { useRouter, useRoute } from 'vue-router'
import CartReview from '@/components/checkout/CartReview.vue'
import ShippingForm from '@/components/checkout/ShippingForm.vue'
import PaymentForm from '@/components/checkout/PaymentForm.vue'
import Confirmation from '@/components/checkout/Confirmation.vue'

const router = useRouter()
const route = useRoute()

const step = computed(() => parseInt(route.query.step as string) || 1)

const steps = {
  1: CartReview,
  2: ShippingForm,
  3: PaymentForm,
  4: Confirmation
}

const currentStepComponent = computed(() => steps[step.value] || CartReview)

function nextStep() {
  if (step.value < 4) {
    router.push({
      name: 'checkout',
      query: { step: step.value + 1 }
    })
  } else {
    // Commande terminée, remplacer pour éviter de revenir au checkout
    router.replace({ name: 'order-success' })
  }
}

function previousStep() {
  if (step.value > 1) {
    router.go(-1)  // Utilise l'historique naturel
  } else {
    router.push({ name: 'cart' })
  }
}
</script>

Guards de navigation

Les guards permettent de contrôler l’accès aux routes et d’exécuter du code avant/après la navigation.

Types de guards

Guard global : beforeEach

// router/index.ts
import { useAuthStore } from '@/stores/auth'

router.beforeEach((to, from, next) => {
  const authStore = useAuthStore()

  // Routes qui nécessitent une authentification
  if (to.meta.requiresAuth && !authStore.isAuthenticated) {
    next({
      name: 'login',
      query: { redirect: to.fullPath }
    })
    return
  }

  // Routes admin
  if (to.meta.requiresAdmin && !authStore.isAdmin) {
    next({ name: 'forbidden' })
    return
  }

  next()
})

Guard global : afterEach

router.afterEach((to, from) => {
  // Mise à jour du titre de la page
  document.title = to.meta.title as string || 'Mon E-commerce'

  // Tracking analytics
  trackPageView(to.fullPath)

  // Scroll to top
  window.scrollTo(0, 0)
})

Guard par route : beforeEnter

const routes: RouteRecordRaw[] = [
  {
    path: '/checkout',
    name: 'checkout',
    component: Checkout,
    beforeEnter: (to, from, next) => {
      const cartStore = useCartStore()

      if (cartStore.isEmpty) {
        next({ name: 'cart', query: { error: 'empty' } })
        return
      }

      next()
    }
  },
  {
    path: '/products/:id',
    name: 'product-detail',
    component: ProductDetail,
    beforeEnter: async (to, from, next) => {
      const productStore = useProductStore()
      const id = parseInt(to.params.id as string)

      // Vérifier si le produit existe
      const exists = await productStore.productExists(id)
      if (!exists) {
        next({ name: 'not-found' })
        return
      }

      next()
    }
  }
]

Guard dans le composant

<script setup lang="ts">
import { onBeforeRouteLeave, onBeforeRouteUpdate } from 'vue-router'

const hasUnsavedChanges = ref(false)

// Appelé quand on quitte ce composant
onBeforeRouteLeave((to, from) => {
  if (hasUnsavedChanges.value) {
    const answer = window.confirm(
      'Vous avez des modifications non sauvegardées. Voulez-vous vraiment quitter ?'
    )
    if (!answer) return false
  }
  return true
})

// Appelé quand la route change mais le composant est réutilisé
onBeforeRouteUpdate((to, from) => {
  // Utile quand on navigue de /products/1 vers /products/2
  loadProduct(to.params.id)
})
</script>

Routes imbriquées (Nested Routes)

Les routes imbriquées permettent de créer des layouts complexes avec des sous-vues.

Configuration

const routes: RouteRecordRaw[] = [
  {
    path: '/account',
    name: 'account',
    component: AccountLayout,
    meta: { requiresAuth: true },
    children: [
      {
        path: '',  // /account
        name: 'account-dashboard',
        component: AccountDashboard
      },
      {
        path: 'profile',  // /account/profile
        name: 'account-profile',
        component: AccountProfile
      },
      {
        path: 'orders',  // /account/orders
        name: 'account-orders',
        component: AccountOrders
      },
      {
        path: 'orders/:orderId',  // /account/orders/123
        name: 'account-order-detail',
        component: OrderDetail,
        props: true
      },
      {
        path: 'settings',  // /account/settings
        name: 'account-settings',
        component: AccountSettings,
        children: [
          {
            path: 'security',  // /account/settings/security
            name: 'account-security',
            component: SecuritySettings
          },
          {
            path: 'notifications',  // /account/settings/notifications
            name: 'account-notifications',
            component: NotificationSettings
          }
        ]
      }
    ]
  }
]

Composant Layout avec RouterView

<!-- layouts/AccountLayout.vue -->
<template>
  <div class="account-layout">
    <aside class="sidebar">
      <nav>
        <RouterLink :to="{ name: 'account-dashboard' }">
          Tableau de bord
        </RouterLink>
        <RouterLink :to="{ name: 'account-profile' }">
          Mon profil
        </RouterLink>
        <RouterLink :to="{ name: 'account-orders' }">
          Mes commandes
        </RouterLink>
        <RouterLink :to="{ name: 'account-settings' }">
          Paramètres
        </RouterLink>
      </nav>
    </aside>

    <main class="content">
      <!-- Les composants enfants sont rendus ici -->
      <RouterView />
    </main>
  </div>
</template>

<style scoped>
.account-layout {
  display: grid;
  grid-template-columns: 250px 1fr;
  min-height: 100vh;
}

.sidebar nav {
  display: flex;
  flex-direction: column;
  gap: 1rem;
  padding: 2rem;
}

.sidebar a.router-link-active {
  color: var(--primary-color);
  font-weight: bold;
}
</style>

Bonnes pratiques

1. Nommez toujours vos routes

// Mauvais - difficile à maintenir
router.push('/products/123')

// Bon - découplé du chemin
router.push({ name: 'product-detail', params: { id: '123' } })

2. Utilisez toujours props: true

Cela rend vos composants testables et réutilisables en dehors du contexte de routing.

3. Typez vos routes avec TypeScript

// types/router.ts
import type { RouteRecordRaw } from 'vue-router'

declare module 'vue-router' {
  interface RouteMeta {
    requiresAuth?: boolean
    requiresAdmin?: boolean
    title?: string
    layout?: 'default' | 'admin' | 'blank'
  }
}

4. Centralisez la logique de navigation

// composables/useNavigation.ts
export function useNavigation() {
  const router = useRouter()

  return {
    goToProduct(id: number) {
      router.push({ name: 'product-detail', params: { id: String(id) } })
    },

    goToCategory(slug: string) {
      router.push({ name: 'category', params: { slug } })
    },

    goToCheckout() {
      router.push({ name: 'checkout' })
    },

    goBack() {
      router.go(-1)
    }
  }
}

5. Gérez les erreurs de navigation

router.push({ name: 'product-detail', params: { id: '123' } })
  .catch(error => {
    if (error.name !== 'NavigationDuplicated') {
      console.error('Erreur de navigation:', error)
    }
  })

6. Utilisez le lazy loading pour les routes

const routes: RouteRecordRaw[] = [
  {
    path: '/admin',
    component: () => import('@/layouts/AdminLayout.vue'),
    children: [
      {
        path: 'dashboard',
        component: () => import('@/views/admin/Dashboard.vue')
      }
    ]
  }
]

Pièges courants

1. Oublier de convertir les paramètres

Les paramètres d’URL sont toujours des chaînes de caractères :

// Piège : comparaison incorrecte
const id = route.params.id  // "123" (string)
products.find(p => p.id === id)  // Ne trouve rien si p.id est un number

// Solution
const id = parseInt(route.params.id as string)
products.find(p => p.id === id)  // Fonctionne correctement

2. Ne pas réagir aux changements de paramètres

Quand on navigue entre /products/1 et /products/2, le composant n’est pas recréé :

<script setup>
// Piège : ne se met pas à jour
onMounted(() => {
  loadProduct(props.id)  // Appelé une seule fois
})

// Solution : watcher
watch(() => props.id, (newId) => {
  loadProduct(newId)
}, { immediate: true })
</script>

3. Utiliser $route dans les templates avec Composition API

<!-- Piège : casse la réactivité -->
<template>
  <div>{{ $route.params.id }}</div>
</template>

<!-- Solution : utiliser useRoute() -->
<script setup>
const route = useRoute()
</script>
<template>
  <div>{{ route.params.id }}</div>
</template>

4. Navigation vers la même route

// Piège : déclenche une erreur NavigationDuplicated
router.push({ name: 'current-route' })

// Solution : vérifier avant de naviguer
if (route.name !== 'current-route') {
  router.push({ name: 'current-route' })
}

// Ou ignorer l'erreur
router.push({ name: 'current-route' }).catch(() => {})

5. Oublier le trailing slash

// Ces routes sont différentes !
{ path: '/products' }   // /products
{ path: '/products/' }  // /products/

// Solution : configuration du router
const router = createRouter({
  strict: true,  // Différencie /products et /products/
  // ou
  strict: false  // Les traite comme identiques (défaut)
})

Conclusion

Vue Router est un outil puissant qui va bien au-delà du simple mapping URL vers composant. Dans cet article, nous avons couvert :

  • Les routes dynamiques avec paramètres simples, multiples et optionnels
  • La différence entre route params et query params et quand utiliser chacun
  • L’option props: true pour découpler vos composants du router
  • La navigation programmatique avec push, replace et go
  • Les guards de navigation pour protéger vos routes
  • Les routes imbriquées pour des layouts complexes

Ces concepts sont essentiels pour construire des applications Vue.js professionnelles. Je vous encourage à expérimenter avec ces fonctionnalités dans vos propres projets, en commençant par un cas d’utilisation simple et en ajoutant progressivement de la complexité.

N’oubliez pas que la documentation officielle de Vue Router est une excellente ressource pour approfondir ces sujets. Bonne programmation !

Advertisement

In-Article Ad

Dev Mode

Share this article

Mahmoud DEVO

Mahmoud DEVO

Senior Full-Stack Developer

I'm a passionate full-stack developer with 10+ years of experience building scalable web applications. I write about Vue.js, Node.js, PostgreSQL, and modern DevOps practices.

Enjoyed this article?

Subscribe to get more tech content delivered to your inbox.

Related Articles