4. Esempio [nuxt-01]: Routing e navigazione
Realizzeremo una serie di semplici esempi per scoprire gradualmente come funziona un'applicazione [nuxt]. Inizieremo portando l'applicazione [vuejs-11] dal documento |Introduzione al framework VUE.JS attraverso esempi|, per scoprire innanzitutto cosa differenzia l'organizzazione del codice di un'applicazione [nuxt] da quella di un'applicazione [vue].
4.1. Struttura della directory del progetto
Il progetto [vuejs-11] era un progetto che prevedeva la navigazione tra le viste:

La struttura delle directory del codice sorgente del progetto [vuejs-11] era la seguente:

- [main.js] era lo script eseguito all'avvio dell'applicazione [vue];
- [router.js] definiva le regole di routing;
- [App.vue] era la vista strutturale dell'applicazione. Organizzava il layout delle diverse viste;
- [Component1, Component2, Component3, Layout, Navigation] erano i componenti utilizzati nelle varie schermate dell'applicazione;
Quando si esegue il porting dell'applicazione [Vue] [1] in un'applicazione [Nuxt] [2]:
- gli script eseguiti all'avvio dell'applicazione devono essere dichiarati nella chiave [plugins] del file [nuxt.config.js]. Inoltre, è possibile separare gli script destinati al server [nuxt] da quelli destinati al client [nuxt];
- la vista [App.vue] deve essere collocata nella cartella [layouts] e rinominata [default.vue];
- i componenti [Component1, Component2, Component3], che sono le destinazioni di routing, devono essere spostati nella cartella [pages]. Uno di essi, quello che funge da home page, deve essere rinominato [index.vue]. Qui abbiamo rinominato i file:
- [Component1] --> [index]: visualizza il testo [Home];
- [Component2] --> [page1]: visualizza il testo [Page 1];
- [Component3] --> [page2]: visualizza il testo [Page 2];
[nuxt] utilizza il contenuto della cartella [pages] per generare dinamicamente i seguenti percorsi:
Di conseguenza, il file [router.js] utilizzato nel progetto [vue] non è più necessario nel progetto [nuxt].
Il file di configurazione [nuxt.config.js] sarà il seguente:
export default {
mode: 'universal',
/*
** Headers of the page
*/
head: {
title: 'Introduction à [nuxt.js]',
meta: [
{ charset: 'utf-8' },
{ name: 'viewport', content: 'width=device-width, initial-scale=1' },
{
hid: 'description',
name: 'description',
content: 'ssr routing loading asyncdata middleware plugins store'
}
],
link: [{ rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }]
},
/*
** Customize the progress-bar color
*/
loading: { color: '#fff' },
/*
** Global CSS
*/
css: [],
/*
** Plugins to load before mounting the App
*/
plugins: [],
/*
** Nuxt.js dev-modules
*/
buildModules: [
// Doc: https://github.com/nuxt-community/eslint-module
'@nuxtjs/eslint-module'
],
/*
** Nuxt.js modules
*/
modules: [
// Doc: https://bootstrap-vue.js.org
'bootstrap-vue/nuxt',
// Doc: https://axios.nuxtjs.org/usage
'@nuxtjs/axios'
],
/*
** Axios module configuration
** See https://axios.nuxtjs.org/options
*/
axios: {},
/*
** Build configuration
*/
build: {
/*
** You can extend webpack config here
*/
extend(config, ctx) {}
},
// source code directory
srcDir: 'nuxt-01',
// router
router: {
// application URL root
base: '/nuxt-01/'
},
// server
server: {
// service port, default 3000
port: 81,
// network addresses listened to, default localhost: 127.0.0.1
// 0.0.0.0 = all the machine's network addresses
host: '0.0.0.0'
}
}
- riga 62: specificare la cartella contenente il codice sorgente del progetto [dvp];
- riga 66: specificare l'URL radice dell'applicazione [dvp] (è possibile inserire quello che si desidera);
- riga 43: notare che la libreria [bootstrap-vue] è referenziata nella configurazione;
4.2. Porting del file [main.js]
Il file [main.js] per il progetto [vuejs-11] era il seguente:
// imports
import Vue from 'vue'
import App from './App.vue'
// plugins
import BootstrapVue from 'bootstrap-vue'
Vue.use(BootstrapVue);
// bootstrap
import 'bootstrap/dist/css/bootstrap.css'
import 'bootstrap-vue/dist/bootstrap-vue.css'
// routeur
import monRouteur from './router'
// configuration
Vue.config.productionTip = false
// instanciation projet [App]
new Vue({
name: "app",
// vue principale
render: h => h(App),
// routeur
router: monRouteur,
}).$mount('#app')
A parte le [importazioni], il codice fa quanto segue:
- righe 5–11: utilizza la libreria [bootstrap-vue]. Questo è ora gestito dal modulo [bootstrap-vue/nuxt] alla riga 43 del file di configurazione [nuxt.config.js];
- righe 14 e 25: utilizzo del file di routing [router.js]. Questo viene ora eseguito automaticamente dall'applicazione [nuxt] in base alla struttura delle directory della cartella [pages];
- righe 20–26: istanziazione della vista principale dell'applicazione. In un'applicazione [nuxt], la vista [layouts/default.vue] funge da vista principale;
Il file [main.js] non è più necessario. Se lo fosse stato, lo avremmo dichiarato nella chiave [plugins] alla riga 30 del file di configurazione [nuxt.config.js];
4.3. La vista principale [default.vue]

La vista principale [layouts/default.vue] è la seguente:
<template>
<div class="container">
<b-card>
<!-- a message -->
<b-alert show variant="success" align="center">
<h4>[nuxt-01] : routage et navigation</h4>
</b-alert>
<!-- the current routing view -->
<nuxt />
</b-card>
</div>
</template>
<script>
export default {
name: 'App'
}
</script>
- Alla riga 9, nel progetto [vuejs-11], avevamo il tag <router-view /> invece del tag <nuxt /> utilizzato qui. Entrambi sembrano funzionare. Li ho provati entrambi senza notare alcuna differenza. Ho mantenuto il tag <nuxt />, che è quello consigliato. Visualizza la vista corrente, ovvero la pagina di destinazione del percorso corrente;
4.4. I componenti

Rispetto al progetto [vuejs-11], i componenti [layout, navigazione] rimangono invariati:
[componenti / layout.vue]
<!-- view layout -->
<template>
<!-- line -->
<div>
<b-row>
<!-- two-column zone -->
<b-col v-if="left" cols="2">
<slot name="left" />
</b-col>
<!-- ten-column zone -->
<b-col v-if="right" cols="10">
<slot name="right" />
</b-col>
</b-row>
</div>
</template>
<script>
export default {
// paramètres
props: {
left: {
type: Boolean
},
right: {
type: Boolean
}
}
}
</script>
Questo componente viene utilizzato per strutturare le pagine dell'applicazione in due colonne:
- righe 7–9: la colonna di sinistra occupa 2 colonne Bootstrap;
- righe 11–13: la colonna di destra occupa 10 colonne Bootstrap;
[navigation.vue]
<template>
<!-- bootstrap menu with three options -->
<b-nav vertical>
<b-nav-item to="/" exact exact-active-class="active">
Home
</b-nav-item>
<b-nav-item to="/page1" exact exact-active-class="active">
Page 1
</b-nav-item>
<b-nav-item to="/page2" exact exact-active-class="active">
Page 2
</b-nav-item>
</b-nav>
</template>
Questo componente visualizza tre link di navigazione:

Per determinare quali valori assegnare agli attributi [to] nelle righe 4, 7 e 10, consultare la cartella [pages] [2]:
- la pagina [index] avrà l'URL [/] ;
- la pagina [page1] avrà l'URL [/page1];
- la pagina [page2] avrà l'URL [/page2];
Il componente [navigation] può anche essere scritto come segue:
<template>
<!-- bootstrap menu with three options -->
<b-nav vertical>
<nuxt-link to="/" exact exact-active-class="active">
Home
</nuxt-link>
<nuxt-link to="/page1" exact exact-active-class="active">
Page 1
</nuxt-link>
<nuxt-link to="/page2" exact exact-active-class="active">
Page 2
</nuxt-link>
</b-nav>
</template>
Il tag <b-nav-item> viene sostituito dal tag <nuxt-link>, che indica un link di routing. In pratica, non ho notato alcuna differenza significativa, nulla che potesse far pendere la bilancia a favore di un tag rispetto all'altro.
4.5. Le pagine

La pagina [index.vue] mostra la seguente vista:

Il codice della pagina è il seguente:
<!-- page principale -->
<template>
<Layout :left="true" :right="true">
<!-- navigation -->
<Navigation slot="left" />
<!-- message-->
<b-alert slot="right" show variant="warning">
Home
</b-alert>
</Layout>
</template>
<script>
/* eslint-disable no-undef */
/* eslint-disable no-console */
/* eslint-disable nuxt/no-env-in-hooks */
import Navigation from '@/components/navigation'
import Layout from '@/components/layout'
export default {
name: 'Home',
// components used
components: {
Layout,
Navigation
},
// life cycle
beforeCreate(...args) {
console.log('[home beforeCreate]', 'process.server=', process.server,
'process.client=', process.client, "nombre d'arguments=", args.length)
},
created(...args) {
console.log('[home created]', 'process.server=', process.server,
'process.client=', process.client, "nombre d'arguments=", args.length)
},
beforeMount(...args) {
console.log('[home beforeMount]', 'process.server=', process.server,
'process.client=', process.client, "nombre d'arguments=", args.length)
},
mounted(...args) {
console.log('[home mounted]', 'process.server=', process.server,
'process.client=', process.client, "nombre d'arguments=", args.length)
}
}
</script>
- riga 5: il componente di navigazione viene posizionato nella colonna di sinistra;
- righe 7–9: un avviso è posizionato nella colonna di destra;
Nella sezione <script>, inseriamo il codice nelle funzioni del ciclo di vita della pagina [beforeCreate, created, beforeMount, beforeMounted]. Vogliamo sapere quali vengono eseguite dal server []nuxt e quali dal client [nuxt]. Ricordiamo due cose:
- quando viene richiesta una pagina — sia all'avvio dell'applicazione (come con la pagina [index]) sia manualmente dall'utente che aggiorna la pagina del browser o digita un URL a mano — questa viene prima fornita dal server [nuxt]. Il server interpreta il codice sopra riportato ed esegue il JavaScript in esso contenuto;
- quando la pagina inviata dal server [nuxt] arriva nel browser, arriva con il codice lato client [nuxt]. Il codice lato client quindi riesegue il codice sopra riportato;
- Utilizziamo i log per tracciare chi sta facendo cosa, al fine di comprendere meglio questo processo;
- Righe 30–31: utilizziamo un oggetto globale [process] nella funzione che esiste sia sul server che sul client:
- [process.server] è vero se il codice viene eseguito dal server, falso in caso contrario;
- [process.client] è vero se il codice viene eseguito dal client, falso in caso contrario;
- Poiché la variabile [process] non è dichiarata nel codice, siamo tenuti a includere la riga 14 per [eslint]. La riga 16 è necessaria perché altrimenti [eslint] segnala un diverso tipo di errore a causa della variabile [process]. La riga 15 è necessaria per consentire l'uso di [console] nelle funzioni del ciclo di vita;
- riga 29: vogliamo anche sapere se le funzioni del ciclo di vita ricevono argomenti. Scopriremo infatti che [nuxt] passa informazioni a determinate funzioni. Vogliamo sapere se le funzioni del ciclo di vita sono tra queste;
- Ripetiamo lo stesso codice per tutte e quattro le funzioni;
4.6. Il file [nuxt.config.js]
Questo file controlla l'esecuzione del progetto [dvp]. È stato descritto a pagina 33.
4.7. Esecuzione del progetto
Eseguiamo il progetto:

La pagina visualizzata è la seguente:

Una volta caricata nel browser, l'applicazione [nuxt] diventa un'applicazione [vue] standard. Non tratteremo quindi il comportamento lato client dell'applicazione [nuxt-01]. Questo argomento è stato trattato nel progetto [vuejs-11] nel documento |Introduzione al framework VUE.JS attraverso esempi|.
L'applicazione [nuxt] differisce dall'applicazione [vue] solo sotto due aspetti:
- l'avvio iniziale dell'applicazione, che visualizza la pagina iniziale;
- ogni volta che l'utente attiva in qualsiasi modo l'aggiornamento del browser;
In entrambi questi casi:
- la pagina richiesta viene fornita dal server;
- la pagina ricevuta viene elaborata dal client;
Diamo un'occhiata ai log di avvio dell'applicazione (F12 nel browser):

- in [1], i log del server (process.server=true). Appaiono preceduti dall'etichetta [Nuxt SSR] (SSR = Server Side Rendered);
- in [2], i log del client nel browser (process.client=true);
Da questi log, possiamo dedurre che:
- il server esegue le funzioni del ciclo di vita [beforeCreate, created];
- il client esegue le funzioni del ciclo di vita [beforeCreate, created, beforeMount, mounted];
- il server ha elaborato la pagina prima del client;
- in entrambi i casi, nessuna delle funzioni eseguite riceve alcun argomento;
Ora diamo un'occhiata al codice sorgente della pagina ricevuta (l'opzione [Codice sorgente della pagina] nel browser):
<!doctype html>
<html data-n-head-ssr>
<head>
<title>Introduction à [nuxt.js]</title>
<meta data-n-head="ssr" charset="utf-8">
<meta data-n-head="ssr" name="viewport" content="width=device-width, initial-scale=1">
<meta data-n-head="ssr" data-hid="description" name="description" content="ssr routing loading asyncdata middleware plugins store">
<link data-n-head="ssr" rel="icon" type="image/x-icon" href="/favicon.ico">
<base href="/nuxt-01/">
....
<link rel="preload" href="/nuxt-01/_nuxt/runtime.js" as="script">
<link rel="preload" href="/nuxt-01/_nuxt/commons.app.js" as="script">
<link rel="preload" href="/nuxt-01/_nuxt/vendors.app.js" as="script">
<link rel="preload" href="/nuxt-01/_nuxt/app.js" as="script">
</head>
<body>
<div data-server-rendered="true" id="__nuxt">
<div id="__layout">
<div class="container">
<div class="card">
<div class="card-body">
<div role="alert" aria-live="polite" aria-atomic="true" align="center" class="alert alert-success">
<h4>[nuxt-01] : routage et navigation</h4>
</div>
<div>
<div class="row">
<div class="col-2">
<ul class="nav flex-column">
<li class="nav-item">
<a href="/nuxt-01/" target="_self" class="nav-link active nuxt-link-active">
Home
</a>
</li>
<li class="nav-item">
<a href="/nuxt-01/page1" target="_self" class="nav-link">
Page 1
</a>
</li>
<li class="nav-item">
<a href="/nuxt-01/page2" target="_self" class="nav-link">
Page 2
</a>
</li>
</ul>
</div> <div class="col-10">
<div role="alert" aria-live="polite" aria-atomic="true" class="alert alert-warning">
Home
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<script>
window.__NUXT__ = (function (a, b, c, d, e, f, g, h, i, j) {
return {
layout: "default", data: [{}], error: null, serverRendered: true,
logs: [
{ date: new Date(1574069600078), args: [a, b, c, d, e, f, g, "(repeated 1 times)"], type: h, level: i, tag: j },
{ date: new Date(1574070938091), args: [a, b, c, d, e, f, g], type: h, level: i, tag: j }
]
}
}("[home beforeCreate]", "process.server=", "true", "process.client=", "false", "nombre d'arguments=", "0", "log", 2, ""));
</script>
<script src="/nuxt-01/_nuxt/runtime.js" defer></script>
<script src="/nuxt-01/_nuxt/commons.app.js" defer></script>
<script src="/nuxt-01/_nuxt/vendors.app.js" defer></script>
<script src="/nuxt-01/_nuxt/app.js" defer></script>
</body>
</html>
Commenti
- La prima cosa che potresti notare è che il codice HTML ricevuto riflette accuratamente ciò che vede l'utente. Questo non era il caso delle applicazioni [Vue], dove il codice sorgente visualizzato era il codice sorgente di un file HTML quasi vuoto. Quello era ciò che il browser aveva ricevuto. Poi il client [Vue] prendeva il controllo e costruiva la pagina che l'utente si aspettava. A quel punto era necessario andare alla scheda [Inspector] negli strumenti di sviluppo del browser (F12) per visualizzare il codice HTML della pagina visualizzata;
- righe 57–67: questo è lo script che visualizzava i log contrassegnati con [Nuxt SSR]. Questi log venivano generati sul lato server e i risultati erano incorporati in uno script incluso nella pagina inviata;
- righe 68–71: gli script che costituiscono il codice lato client eseguito nel browser;
Gli script nelle righe 68–71 vengono eseguiti e trasformano la pagina ricevuta. Per vedere la pagina che viene infine visualizzata all'utente, vai alla scheda [Inspector] negli strumenti di sviluppo del browser (F12):

Quando si espande il tag <html> [3], si vede il seguente contenuto:
<head>
<title>Introduction à [nuxt.js]</title>
<meta data-n-head="ssr" charset="utf-8">
<meta data-n-head="ssr" name="viewport" content="width=device-width, initial-scale=1">
<meta data-n-head="ssr" data-hid="description" name="description" content="ssr routing loading asyncdata middleware plugins store">
<link data-n-head="ssr" rel="icon" type="image/x-icon" href="/favicon.ico">
<base href="/nuxt-01/">
...
<link rel="preload" href="/nuxt-01/_nuxt/runtime.js" as="script">
<link rel="preload" href="/nuxt-01/_nuxt/commons.app.js" as="script">
<link rel="preload" href="/nuxt-01/_nuxt/vendors.app.js" as="script">
<link rel="preload" href="/nuxt-01/_nuxt/app.js" as="script">
<script charset="utf-8" src="/nuxt-01/_nuxt/pages_index.js"></script>
<script charset="utf-8" src="/nuxt-01/_nuxt/pages_page1.js"></script>
<script charset="utf-8" src="/nuxt-01/_nuxt/pages_page2.js"></script>
</head>
<body>
<div id="__nuxt">
<div id="__layout">
<div class="container">
<div class="card">
<div class="card-body">
<div role="alert" aria-live="polite" aria-atomic="true" class="alert alert-success" align="center">
<h4>[nuxt-01] : routage et navigation</h4>
</div>
<div>
<div class="row">
<div class="col-2">
<ul class="nav flex-column">
<li class="nav-item">
<a href="/nuxt-01/" target="_self" class="nav-link active nuxt-link-active">
Home
</a>
</li>
<li class="nav-item">
<a href="/nuxt-01/page1" target="_self" class="nav-link">
Page 1
</a>
</li>
<li class="nav-item">
<a href="/nuxt-01/page2" target="_self" class="nav-link">
Page 2
</a>
</li>
</ul>
</div>
<div class="col-10">
<div role="alert" aria-live="polite" aria-atomic="true" class="alert alert-warning">
Home
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<script>
window.__NUXT__ = (function (a, b, c, d, e, f, g, h, i) {
return {
layout: "default", data: [{}], error: null, serverRendered: true,
logs: [
{ date: new Date(1574068674481), args: ["[home beforeCreate]", a, b, c, d, e, f], type: g, level: h, tag: i },
{ date: new Date(1574068674482), args: ["[home created]", a, b, c, d, e, f], type: g, level: h, tag: i }
]
}
}("process.server=", "true", "process.client=", "false", "nombre d'arguments=", "0", "log", 2, ""));
</script>
<script src="/nuxt-01/_nuxt/runtime.js" defer=""></script>
<script src="/nuxt-01/_nuxt/commons.app.js" defer=""></script>
<script src="/nuxt-01/_nuxt/vendors.app.js" defer=""></script>
<script src="/nuxt-01/_nuxt/app.js" defer=""></script>
<iframe id="mc-sidebar-container" ...></iframe>
<iframe id="mc-topbar-container"...> </iframe>
<iframe id="mc-toast-container" ...></iframe>
<iframe id="mc-download-overlay-container"...></iframe>
</body>
Commenti
- A prima vista, la pagina visualizzata nelle righe 19–59 sembra essere la stessa della pagina ricevuta;
- Righe 14–16: compaiono tre nuovi script, uno per ciascuna delle pagine dell’applicazione;
- Righe 76–79: compaiono quattro elementi [iframe];
Righe 33, 37 e 42: i link sono problematici. Sembrano link normali che, se cliccati, inviano una richiesta al server. Tuttavia, all'esecuzione, vediamo che non è così: nessuna richiesta viene inviata al server. Per capire perché, dobbiamo tornare alla scheda [Inspector] del browser:

Notiamo che ai link sono stati associati eventi [1, 2]. Sono gli script alle righe 71–74 ad aver associato i gestori di eventi ai link. Pertanto:
- la pagina visualizzata dal client è visivamente identica a quella inviata dal server;
- il comportamento dinamico è stato aggiunto alla pagina dal client;
Ora richiediamo la pagina [page1] digitando manualmente l'URL [http://192.168.1.128:81/nuxt-01/page1]. I log diventano i seguenti:

Otteniamo gli stessi risultati della pagina [index], ma per la pagina [page1]. Il codice sorgente della pagina ricevuta è il seguente:
<body>
<div data-server-rendered="true" id="__nuxt">
<div id="__layout">
<div class="container">
<div class="card">
<div class="card-body">
<div role="alert" aria-live="polite" aria-atomic="true" align="center" class="alert alert-success"><h4>[nuxt-01] : routage et navigation</h4></div> <div>
<div class="row">
<div class="col-2">
<ul class="nav flex-column">
<li class="nav-item">
<a href="/nuxt-01/" target="_self" class="nav-link">
Home
</a>
</li>
<li class="nav-item">
<a href="/nuxt-01/page1" target="_self" class="nav-link active nuxt-link-active">
Page 1
</a>
</li>
<li class="nav-item">
<a href="/nuxt-01/page2" target="_self" class="nav-link">
Page 2
</a>
</li>
</ul>
</div> <div class="col-10">
<div role="alert" aria-live="polite" aria-atomic="true" class="alert alert-primary">
Page 1
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<script>window.__NUXT__ = { layout: "default", data: [{}], error: null, serverRendered: true, logs: [{ date: new Date(1573917721122), args: ["[page1 beforeCreate]", "process.server=", "true", "process.client=", "false", "nombre d'arguments=", "0"], type: "log", level: 2, tag: "" }] };</script>
<script src="/nuxt-01/_nuxt/runtime.js" defer></script>
<script src="/nuxt-01/_nuxt/commons.app.js" defer></script>
<script src="/nuxt-01/_nuxt/vendors.app.js" defer></script>
<script src="/nuxt-01/_nuxt/app.js" defer></script>
</body>
Otteniamo lo stesso tipo di pagina della pagina [index], ma con l'avviso di visualizzazione [Pagina 1] (riga 30). Righe 41–44: il codice lato client è stato inviato insieme alla pagina. In definitiva, richiedere manualmente un URL equivale a riavviare l'applicazione. L'unica differenza è che la pagina visualizzata non è necessariamente la home page, ma quella che è stata richiesta. Una volta ricevuta la pagina, il client prende il controllo. Il server non verrà più contattato a meno che l'utente non decida diversamente.