Guida Completa a Vue Router: Gestione delle Rotte in Single Page Application

Intermedio
JavaScript Vue.js

Impara a gestire la navigazione nelle tue app Vue.js con Vue Router: dalle rotte dinamiche ai Navigation Guards e alla gestione dell'autenticazione.

Pubblicato
Tag
javascript frontend routing Vue.js SPA Vue Router

Introduzione a Vue Router

In un'applicazione web tradizionale, ogni volta che l'utente clicca su un link, il browser invia una richiesta al server, il quale risponde con un nuovo documento HTML completo. Questo processo causa un ricaricamento della pagina che interrompe l'esperienza utente e aumenta il carico sul server.

Le Single Page Application (SPA), come quelle create con Vue.js, cambiano radicalmente questo paradigma. In una SPA, viene caricata una sola pagina HTML iniziale; successivamente, Vue Router intercetta i cambiamenti dell'URL e aggiorna dinamicamente solo le parti della pagina necessarie, senza mai ricaricare l'intero documento. Questo risultato è possibile grazie all'API history del browser, che permette di manipolare l'URL senza innescare un refresh.

Vue Router è l'router ufficiale di Vue.js e fornisce gli strumenti necessari per mappare gli URL a componenti specifici, gestire parametri dinamici e proteggere l'accesso a determinate aree dell'applicazione.

Installazione e Configurazione Iniziale

Per iniziare a utilizzare Vue Router, è necessario installare il pacchetto tramite npm o yarn. Se stai utilizzando Vue CLI o Vite, puoi selezionare l'opzione router durante la creazione del progetto, oppure installarlo manualmente.

npm install vue-router@4

Una volta installato, il primo passo è definire le rotte. Le rotte sono essenzialmente un array di oggetti JavaScript dove ogni oggetto associa un percorso (path) a un componente Vue.

Definizione del Router

Ecco come configurare un'istanza di base del router in un file dedicato (solitamente src/router/index.js):

import { createRouter, createWebHistory } from 'vue-router';
import HomeView from '../views/HomeView.vue';
import AboutView from '../views/AboutView.vue';
import UserProfile from '../views/UserProfile.vue';

const routes = [
  {
    path: '/',
    name: 'Home',
    component: HomeView
  },
  {
    path: '/about',
    name: 'About',
    component: AboutView
  },
  {
    path: '/user/:id',
    name: 'User',
    component: UserProfile,
    props: true // Passa il parametro :id come prop al componente
  }
];

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

export default router;

In questo esempio, abbiamo utilizzato createWebHistory(), che crea URL "puliti" (es. /about invece di /#/about). È importante notare che l'uso della History API richiede una configurazione lato server per reindirizzare tutte le richieste alla index.html, altrimenti l'utente riceverà un errore 404 ricaricando la pagina su un percorso specifico.

Componenti Fondamentali: RouterView e RouterLink

Dopo aver configurato il router, dobbiamo integrarlo nel nostro template HTML. Vue Router mette a disposizione due componenti chiave.

RouterView

Il componente <router-view /> funge da segnaposto. È il punto esatto in cui Vue Router renderizzerà il componente associato alla rotta attualmente attiva. Se hai un layout con un header e un footer fissi, inserirai il router-view nel contenuto centrale.

RouterLink

Per navigare tra le pagine, non dobbiamo usare il tag <a> standard, poiché questo causerebbe il ricaricamento della pagina. Utilizziamo invece <router-link>, che genera un link interno ottimizzato.

<nav>
  <!-- Navigazione tramite path -->
  <router-link to="/">Home</router-link>
  <!-- Navigazione tramite nome della rotta (best practice) -->
  <router-link :to="{ name: 'About' }">Chi Siamo</router-link>
</nav>

<main>
  <router-view />
</main>

L'uso del nome della rotta (name) invece del percorso (path) è fortemente raccomandato. Se in futuro deciderai di cambiare l'URL di /about in /chi-siamo, non dovrai aggiornare tutti i link nel progetto, poiché il nome rimarrà invariato.

Rotte Dinamiche e Parametri

Molte applicazioni richiedono pagine basate su dati variabili, come un profilo utente o il dettaglio di un prodotto. In questi casi si utilizzano le rotte dinamiche.

Parametri di Rotta

Come visto nell'esempio di configurazione, un parametro si definisce con i due punti: :id. Quando l'utente visita /user/123, Vue Router cattura 123 come valore del parametro id.

Per accedere a questo valore all'interno del componente, possiamo usare l'oggetto $route nel template o useRoute nella Composition API.

<template>
  <div>
    <h1>Profilo Utente</h1>
    <p>Stai visualizzando l'utente con ID: {{ userId }}</p>
  </div>
</template>

<script setup>
import { useRoute } from 'vue-router';
import { computed } from 'vue';

const route = useRoute();
const userId = computed(() => route.params.id);
</script>

Props per i Componenti di Rotta

Per rendere i componenti più riutilizzabili e meno dipendenti dall'oggetto $route (che rende i test più difficili), possiamo abilitare l'opzione props: true nella definizione della rotta. In questo modo, il parametro :id verrà passato direttamente come prop al componente, permettendogli di essere usato come un normale componente Vue.

Navigazione Programmatica e Navigation Guards

Non sempre la navigazione avviene tramite un click su un link. Spesso è necessario reindirizzare l'utente dopo un'azione, come l'invio di un modulo di login.

Navigazione via Codice

Utilizzando l'hook useRouter (da non confondere con useRoute), possiamo spostare l'utente programmaticamente:

import { useRouter } from 'vue-router';

const router = useRouter();

const login = async () => {
  const success = await authService.login(credentials);
  if (success) {
    // Reindirizza alla dashboard
    router.push({ name: 'Dashboard' });
  } else {
    alert('Errore di accesso');
  }
};

Proteggere le Rotte con i Navigation Guards

I Navigation Guards sono middleware che permettono di intercettare la navigazione prima che questa avvenga. Sono fondamentali per implementare l'autenticazione.

Il guard più comune è beforeEach, che viene eseguito globalmente prima di ogni cambio di rotta.

router.beforeEach((to, from, next) => {
  const isAuthenticated = localStorage.getItem('user-token');
  const requiresAuth = to.matched.some(record => record.meta.requiresAuth);

  if (!isAuthenticated && requiresAuth) {
    // Se la rotta richiede auth e l'utente non è loggato, vai al login
    next({ name: 'Login' });
  } else {
    // Altrimenti prosegui normalmente
    next();
  }
});

Per far funzionare questo sistema, aggiungiamo un oggetto meta alle rotte che vogliamo proteggere:

{
  path: '/admin',
  component: AdminPanel,
  meta: { requiresAuth: true }
}

Esempi Pratici e Casi d'Uso Reali

Caso 1: Gestione del 404 (Pagina non trovata)

Per gestire gli URL che non corrispondono a nessuna rotta definita, è possibile aggiungere una rotta "catch-all" alla fine dell'array delle rotte.

{
  path: '/:pathMatch(.*)*',
  name: 'NotFound',
  component: NotFoundView
}

Caso 2: Rotte Annidate (Nested Routes)

Immaginiamo un pannello di controllo dove l'header e il menu laterale rimangono fissi, ma il contenuto centrale cambia tra "Impostazioni", "Statistiche" e "Utenti".

{
  path: '/settings',
  component: SettingsLayout,
  children: [
    { path: 'profile', component: ProfileSettings },
    { path: 'security', component: SecuritySettings }
  ]
}

In questo caso, SettingsLayout deve contenere un proprio <router-view /> dove verranno renderizzati i componenti figli.

Errori Comuni e FAQ

Perché ricevo un 404 quando ricarico la pagina in produzione?

Questo è l'errore più comune con createWebHistory. Poiché l'app è una SPA, il server riceve una richiesta per /about, ma sul server non esiste un file chiamato about. Devi configurare il tuo server (Nginx, Apache, Vercel, ecc.) affinché ogni richiesta che non trova un file fisico venga reindirizzata a index.html.

Qual è la differenza tra router.push e router.replace?

  • router.push: Aggiunge l'indirizzo alla cronologia del browser. Se l'utente clicca "Indietro", torna alla pagina precedente.
  • router.replace: Sostituisce l'indirizzo corrente nella cronologia. È utile per le pagine di login o di successo dopo un pagamento, dove non vuoi che l'utente torni indietro alla pagina di transazione.

Come posso passare dati tra rotte senza usare l'URL?

Sebbene i parametri siano l'ideale, a volte si vogliono passare oggetti complessi. In quel caso, è consigliabile utilizzare un sistema di state management come Pinia (il successore di Vuex) oppure passare i dati tramite un servizio condiviso, poiché l'URL deve rimanere leggibile e condivisibile.

Prossimi Passi

Ora che hai una solida base su Vue Router, puoi approfondire i seguenti argomenti per rendere le tue applicazioni professionali:

  1. Lazy Loading: Invece di importare tutti i componenti all'inizio, usa le importazioni dinamiche () => import('../views/Home.vue') per dividere il codice in piccoli bundle e velocizzare il caricamento iniziale.
  2. Scroll Behavior: Implementa la funzione scrollBehavior nel router per decidere se la pagina deve tornare in cima o mantenere la posizione dello scroll durante la navigazione.
  3. Integrazione con Pinia: Impara a sincronizzare lo stato dell'autenticazione globale con i Navigation Guards per una gestione fluida degli accessi.
  4. Transizioni: Usa il componente <transition> di Vue insieme a <router-view> per creare animazioni fluide durante il cambio di pagina.