back to top

Come cambia Vue Router in Vue 3

Con l’introduzione di Vue 3 è stato aggiornato anche Vue Router la cui API resta sostanzialmente invariata a parte alcune modifiche minori di cui discuteremo in questa lezione. È bene sottolineare che Vue Router passa dalla versione 3 (per Vue 2.x) alla versione 4 (per Vue 3.x). Su questo tema si può dire che forse sarebbe stato meglio unificare il numero di versione dei vari componenti del framework per rendere il tutto più omogeneo e creare meno confusione fra le diverse versioni.

Come creare un’applicazione con createRouter()

Il team di sviluppo di Vue ha invece deciso di uniformare il modo in cui vengono creati e configurati i diversi pezzi di un’applicazione.

Nelle precedenti lezioni abbiamo infatti visto che in Vue 3 non viene più istanziato un nuovo oggetto radice di tipo Vue, al contrario è stata introdotta la funzione createApp() che restituisce un’istanza base per l’intera applicazione.

Allo stesso modo per Vue Router useremo la funzione createRouter() invece di new Router().

Per comprendere al meglio come realizzare un’applicazione in Vue 3 con l’ausilio di Vue Router 4, facciamo uso di vue cli e partiamo da un semplice esempio che generiamo con il comando vue create router-demo-v3.

Selezioniamo poi l’opzione per scegliere manualmente le diverse funzioni da includere nell’applicazione.

Step 1 della configurazione di una nuova applicazione tramite Vue Cli

Assicuriamoci di aver contrassegnato ‘Router’ tramite la barra spaziatrice e premiamo INVIO per andare avanti con la procedura.

Step 2 della configurazione di una nuova applicazione tramite Vue Cli

Sempre tramite i tasti freccia selezioniamo Vue 3.x come versione del nostro progetto e premiamo nuovamente INVIO.

Step 3 della configurazione di una nuova applicazione tramite Vue Cli

Al passo successivo scegliamo di usare la modalità ‘history’ per il router che ci permette di ottenere i diversi percorsi degli url senza il carattere ‘#’. In questo modo invece di avere degli indirizzi del tipo ‘https://example.com/#contatti’, avremo semplicemente ‘https://example.com/contatti’. Come ci suggerisce Vue CLI dovremo però assicurarci di configurare correttamente i server se necessario.

Step 4 della configurazione di una nuova applicazione tramite Vue Cli

Continuiamo con il processo di inizializzazione dell’applicazione e scegliamo poi ESLint + Prettier per la formattazione e la segnalazione di eventuali errori nel codice.

Step 5 della configurazione di una nuova applicazione tramite Vue Cli

Chiediamo quindi di effettuare dei controlli sul codice ad ogni salvataggio.

Step 6 della configurazione di una nuova applicazione tramite Vue Cli

E salviamo le diverse configurazioni in file separati.

Step 7 della configurazione di una nuova applicazione tramite Vue Cli

Per concludere, non salviamo le impostazioni scelte per progetti futuri e, dopo aver premuto il tasto INVIO, aspettiamo che Vue CLI completi la procedura di inizializzazione scaricando ed installando tutti i pacchetti necessari.

Step 8 della configurazione di una nuova applicazione tramite Vue Cli

A questo punto possiamo analizzare le differenze più evidenti rispetto alla precedente versione di Vue router andando ad aprire il file src/router/index.js.

import { createRouter, createWebHistory } from "vue-router";
  import Home from "../views/Home.vue";

  const routes = [
    {
      path: "/",
      name: "Home",
      component: Home
    },
    {
      path: "/about",
      name: "About",
      // route level code-splitting
      // this generates a separate chunk (about.[hash].js) for this route
      // which is lazy-loaded when the route is visited.
      component: () =>
        import(/* webpackChunkName: "about" */ "../views/About.vue")
    }
  ];

  const router = createRouter({
    history: createWebHistory(process.env.BASE_URL),
    routes
  });

  export default router;

Selezionare il tipo di percorso con createWebHistory()

Notiamo che vengono importate due funzioni createRouter e createWebHistory. La prima consente di ottenere un nuovo router attraverso un oggetto di opzioni che comprende un array di percorsi (routes) e una proprietà history a cui assegniamo il valore restituito dalla funzione createWebHistory. Questa riceve come argomento una stringa che rappresenta il percorso base della nostra applicazione. Se ricordiamo bene, in fase di creazione dell’applicazione con Vue CLI avevamo scelto la modalità ‘history’ per l’applicazione. Con le poche righe di codice appena viste, Vue CLI genera il codice per configurare direttamente il router nella modalità selezionata.

L’opzione history rimpiazza quella che in Vue 2 era la proprietà mode: "history".

A seconda della modalità in cui si intende usare il router, in Vue 3 possiamo invece utilizzare una delle seguenti 3 opzioni:

  • history: createWebHistory()
  • hash: createWebHashHistory() se vogliamo invece avere dei percorsi prefissati dal carattere ‘#’.
  • abstract: createMemoryHistory() che può risultare particolarmente utile in fase di test dell’applicazione.

Abilitare le funzionalità del router nell’applicazione

In Vue 2 avevamo un’istanza base di tipo Vue e, nelle applicazioni che facevano uso di router, passavamo la nuova istanza del router stesso come una delle opzioni per configurare l’istanza base.

// codice per Vue 2
  new Vue({
    router,
    render: h => h(App)
  }).$mount("#app");

Al contrario, in Vue 3 ci serviamo della funzione use per configurare l’istanza base dell’applicazione creata con createApp().

import { createApp } from "vue";
  import App from "./App.vue";
  import router from "./router";

  createApp(App)
    .use(router)
    .mount("#app");

Intercettare tutti i percorsi in Vue Router 4

Un’altra modifica significativa nell’ultima versione di Vue Router riguarda il modo in cui intercettare qualsiasi percorso. In precedenza si usava il carattere ‘*’ che veniva spesso sfruttato per attivare un componente al verificarsi dell’errore 404, ovvero per mostrare uno specifico componente nel caso nessun altro oggetto di configurazione dei percorsi avesse intercettato l’url immesso dall’utente (Ricordiamo che Vue Router passa in rassegna gli oggetti di configurazione dei percorsi in ordine e si ferma non appena incontra un oggetto con una proprietà path che soddisfa il percorso corrente).

import Vue from "vue";
import VueRouter from "vue-router";
import Home from "../views/Home.vue";
import NotFound from "../views/NotFound.vue";

Vue.use(VueRouter);

const routes = [
  {
    path: "/",
    name: "Home",
    component: Home,
  },
  {
    path: "/about",
    name: "About",
    // route level code-splitting
    // this generates a separate chunk (about.[hash].js) for this route
    // which is lazy-loaded when the route is visited.
    component: () =>
      import(/* webpackChunkName: "about" */ "../views/About.vue"),
  },
  { path: "*", component: NotFound },
];

Nella nuova versione di Vue Router non è più possibile usare il carattere ‘*’ come valore della proprità path, al contrario dovremo usare dei parametri con una espressione regolare personalizzata.

Ma prima di capire di cosa si tratta, dobbiamo fare una veloce digressione e parlare dei parametri per i percorsi dinamici e delle espressioni regolari per tali parametri.

Parametro per percorsi dinamici ed espressioni regolari

Quando abbiamo introdotto Vue Router abbiamo visto che è possibile configurare dei percorsi che presentano dei parametri. Per esempio per gli URL del tipo /articolo/1 o /articolo/2 possiamo inserire nell’array dei percorsi routes un oggetto che intercetti entrambi e attivi lo stesso componente. Questo riceverà poi in ingresso il parametro corrispondente al numero dell’articolo.

Per situazioni simili Vue Router offre una sintassi particolare in cui la parte variabile di un percorso viene catturata da un parametro. Per specificare quest’ultimo prefissiamo i due punti al nome del parametro.

const routes = [
  // segmento del percorso dinamico contraddistinto dalla sintassi
  // ':param'
  { path: '/articolo/:id', component: Article },
]

Il componente Article avrà poi accesso al parametro ricevuto tramite $route.params.id.

const Article = {
  template: '<h2>Articolo {{ $route.params.id }}</h2>',
}

Ci sono casi in cui però abbiamo la necessità di specificare esattamente qual è il tipo dei parametri affinché possano considerarsi validi. Facendo riferimento all’esempio visto in precedenza, potremmo permettere solo id numerici per identificare un certo articolo. Per questo motivo possiamo usare delle espressioni regolari che andremo ad inserire all’interno di parentesi tonde come mostrato nell’esempio sottostante.

const routes = [
  // segmento del percorso dinamico contraddistinto dalla sintassi
  // ':param' con espressione regolare per i soli id numerici
  { path: '/articolo/:id(\d+)', component: Article },
]

In questo modo Vue Router attiverà il componente Article solo per percorsi /articolo/id in cui il parametro ‘id’ deve essere un valore numerico (Per esempio /articolo/1, ma non /articolo/titolo-1 ). In caso contrario non ci sarà una corrispondenza e Vue Router cercherà se esistono altri oggetti nell’array routes che intercettano il percorso specificato dall’utente. Se non esiste un oggetto di routes che soddisfa l’URL indicato, verrà mostrato un avviso all’interno della console degli strumenti per sviluppatori. È importante ricordare di prefissare la sequenza ‘d’ con un’altra barra rovesciata (backslash) (‘\d’) in modo che la stringa finale, che sarà esaminata da Vue Router, contenga un carattere ” e quindi la sequenza ‘d’ sotto forma di stringa (Ciò dipende dal modo in cui vengono interpretate delle sequenze di escape all’interno delle stringhe in Javascript).

Parametri ripetibili

Abbiamo visto come specificare tramite espressioni regolari delle restrizioni per i parametri.

Nel caso in cui volessimo intercettare dei percorsi con più segmenti dinamici che si ripetono come articolo/2021/2/1, possiamo indicare che un parametro può presentarsi più volte. Useremo il carattere ‘*’ per specificare che un parametro può ripetersi 0 o più volte oppure ‘+’ per parametri ripetibili 1 o più volte.

const routes = [
  // /articolo/:title+ -> intercetta /articolo/parte1, 
  // /articolo/parte1/parte2, /articolo/parte1/parte2/parte3, ecc...
  { path: '/articolo/:title+' },
  // /articolo/:title* -> intercetta /articolo, 
  // /articolo/parte1/parte2, /articolo/parte1/parte2/parte3, ecc...
  { path: '/:articolo*' },
]

Possiamo allora combinare le due funzionalità appena viste per ottenere un parametro solo di tipo numerico che può ripetersi una o più volte.

const routes = [
  // valido per percorsi come /articolo/1, /articolo/2020/1, 
  // /articolo/2021/3/2
  { path: '/articolo/:id(\d+)+', component: Article },
]

In base a quanto abbiamo appena esposto, dovrebbe essere abbastanza lineare aggiungere un oggetto all’array dei percorsi che intercetti tutti gli URL. Possiamo infatti usare un parametro ripetibile con un’espressione regolare.

import Vue from "vue";
import VueRouter from "vue-router";
import Home from "../views/Home.vue";
import NotFound from "../views/NotFound.vue";

Vue.use(VueRouter);

const routes = [
  {
    path: "/",
    name: "Home",
    component: Home,
  },
  {
    path: "/about",
    name: "About",
    // route level code-splitting
    // this generates a separate chunk (about.[hash].js) for this route
    // which is lazy-loaded when the route is visited.
    component: () =>
      import(/* webpackChunkName: "about" */ "../views/About.vue"),
  },
  // per ogni altro percorso, attiva il componente 'NotFound'
  { path: "/:allOtherPaths(.*)*", name:"not-found", component: NotFound },
];

Nell’esempio riportato sopra, abbiamo aggiunto un oggetto per intercettare tutti i percorsi diversi da ‘/’ o ‘/about’. Abbiamo usato un parametro che abbiamo deciso di nominare ‘allOtherPaths’. Tale parametro accetta qualsiasi tipo di carattere e può ripetersi una o più volte.

All’interno del componente NotFound avremo poi accesso, tramite $route.params.allOtherPaths, ad un array contenente i diversi segmenti del percorso. Per esempio se visitiamo l’indirizzo http://localhost:8080/article/2021/3/4, allOtherPaths sarà pari a [ "article", "2021", "3", "4" ].

Transizioni per cambio di pagina

Per concludere questa lezione illustriamo brevemente un’altra importante differenza dell’ultima versione di Vue Router.

Abbiamo già visto come applicare una semplice transizione in ingresso e uscita quando cambia un percorso e viene attivato un diverso componente. In Vue Router 4 cambia il modo in cui configurare le transizioni per i percorsi.

Nella versione precedente racchiudevamo <router-view>, che rappresentava il segnaposto in cui venivano caricati i diversi componenti al cambiare del percorso, fra i tag di apertura e chiusura di <transition>.

<!-- Precedente versione di Vue Router -->
<transition name="fade" mode="out-in">
  <router-view/>
</transition>

Nella nuova versione è stata cambiata l’implementazione di <router-view>. Senza addentrarci troppo nei dettagli, diciamo che dobbiamo invertire la posizione dei due componenti <router-view> e <transition>

<router-view v-slot="{ Component }">
  <transition name="fade" mode="out-in">
    <component :is="Component" />
  </transition>
</router-view>

<transition> racchiude comunque il componente che deve essere attivato e fa uso di <component>, che avevamo già incontrato quando abbiamo parlato dei componenti dinamici. Tramite la proprietà is viene stabilito di volta in volta quale componente deve essere montato. A selezionare il componente attivo è sempre <router-view> che sfrutta la v-slot API e il meccanismo dei cosiddetti scoped slot (ne abbiamo parlato nella lezione sugli slot) per passare il componente corrente a <component>. Nell’esempio riportato sopra, abbiamo usato la destrutturazione degli oggetti di Javascript per prelevare solo l’istanza del componente a cui siamo interessati.

Dobbiamo poi assicurarci di aver implementato le regole CSS opportune affiché le transinzioni funzionino correttamente ricordando che in Vue 3 sono stati aggiornati i nomi delle classi CSS che vengono automaticamente applicate nelle fasi di ingresso e uscita di un componente dal DOM.

<template>
  <div id="nav">
    <router-link to="/">Home</router-link> |
    <router-link to="/about">About</router-link>
  </div>
  <router-view v-slot="{ Component }">
    <transition name="fade" mode="out-in">
      <component :is="Component" />
    </transition>
  </router-view>
</template>

<style>
.fade-enter-active,
.fade-leave-active {
  transition: opacity 500ms ease-out;
}

.fade-enter-from,
.fade-leave-to {
  opacity: 0;
}

/* Altre regole css */
</style>
Pubblicitร