3. Una prima applicazione [nuxt.js]
3.1. Creazione dell’applicazione
Per i nostri sviluppi [nuxt.js] continuiamo a utilizzare VS Code. Abbiamo creato una cartella [dvp] vuota in cui inseriremo i nostri esempi. Quindi apriamo questa cartella:

Salviamo l’area di lavoro con il nome [intro-nuxtjs] [3-5]:

Apriamo un terminale [6-7]:

Finora abbiamo utilizzato il gestore di pacchetti JavaScript [npm]. Per cambiare, useremo qui il gestore [yarn]. Questo è installato, come [npm], con le versioni recenti di [node.js]. Per creare una prima applicazione [nuxt], utilizziamo il comando [yarn create nuxt-app <dossier>] [1]. Il comando richiederà alcune informazioni sul progetto da generare e, una volta ottenute, lo genererà [2]:

In [2] è stata creata un’intera struttura ad albero di file. Il file [package.json] fornisce l’elenco delle librerie JavaScript scaricate nella cartella [node-modules] [4]:
{
"name": "nuxt-intro",
"version": "1.0.0",
"description": "nuxt-intro",
"author": "serge-tahe",
"private": true,
"scripts": {
"dev": "nuxt",
"build": "nuxt build",
"start": "nuxt start",
"generate": "nuxt generate",
"lint": "eslint --ext .js,.vue --ignore-path .gitignore ."
},
"dependencies": {
"nuxt": "^2.0.0",
"bootstrap-vue": "^2.0.0",
"bootstrap": "^4.1.3",
"@nuxtjs/axios": "^5.3.6"
},
"devDependencies": {
"@nuxtjs/eslint-config": "^1.0.1",
"@nuxtjs/eslint-module": "^1.0.0",
"babel-eslint": "^10.0.1",
"eslint": "^6.1.0",
"eslint-plugin-nuxt": ">=0.4.2",
"eslint-config-prettier": "^4.1.0",
"eslint-plugin-prettier": "^3.0.1",
"prettier": "^1.16.4"
}
}
Questo file riflette le risposte fornite al comando [create nuxt-app] per definire il progetto creato (novembre 2019). Il lettore potrebbe avere un file [package.json] diverso:
- potrebbe aver fornito risposte diverse alle domande;
- il comando [create nuxt-app] potrebbe essere cambiato rispetto a quando è stato scritto questo documento: le dipendenze e le versioni potrebbero essere cambiate;
La riga 8 dello script è il comando che avvia l’applicazione:

- in [4], si vede che l’applicazione è disponibile all’indirizzo URL [localhost:3000];
- in [5-6], si vede che l’applicazione genera un server [6] e un client (di quel server) [5];
Proviamo a richiamare URL [http://localhost:3000/] in un browser:

3.2. Descrizione della struttura ad albero di un’applicazione [nuxt]
Riprendiamo la struttura ad albero dell’applicazione creata:

Il ruolo delle cartelle è il seguente:
assets | risorse non compilate dell'applicazione (immagini, ...); |
static | i file di questa cartella saranno disponibili nella directory principale dell’applicazione. In questa cartella si inseriscono i file che devono trovarsi nella directory principale dell’applicazione, come ad esempio il file [robots.txt] destinato ai motori di ricerca; |
components | i componenti [vue] dell’applicazione utilizzati nei file [layouts] e [pages]; |
layout | i componenti [vue] dell’applicazione che fungono da layout per i [pages]; |
pagine | i componenti [vue] visualizzati dai diversi percorsi dell’applicazione. Si potrebbero definire le viste dell’applicazione. Le pagine svolgono un ruolo particolare in [nuxt]: i percorsi vengono creati dinamicamente a partire dalla struttura ad albero presente nella cartella [pages]; |
middleware | gli script eseguiti ad ogni cambio di route. Consentono di controllarle; |
plugin | ha un nome che può creare confusione. Può contenere plugin ma anche script classici. Gli script presenti in questa cartella vengono eseguiti all’avvio dell’applicazione; |
store | se contiene uno script [index.js], quest'ultimo definisce un'istanza dello store di [Vuex]; |
Se una cartella è vuota, è possibile eliminarla dalla struttura ad albero. Nell’esempio sopra riportato, le cartelle [assets, static, middleware, plugins, store] e [2] possono essere eliminate.
3.3. Il file di configurazione [nuxt.config]
L’esecuzione dell’applicazione è controllata dal seguente file [nuxt.config.js]:
export default {
mode: 'universal',
/*
** Headers of the page
*/
head: {
title: process.env.npm_package_name || '',
meta: [
{ charset: 'utf-8' },
{ name: 'viewport', content: 'width=device-width, initial-scale=1' },
{
hid: 'description',
name: 'description',
content: process.env.npm_package_description || ''
}
],
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) {}
}
}
- riga 2: il tipo di applicazione generata:
- [universal]: applicazione client/server. Al caricamento iniziale dell’applicazione e ad ogni aggiornamento della pagina nel browser, il server viene interpellato per fornire la pagina;
- [sap]: applicazione di tipo [Single Page Application]: un server fornisce inizialmente l’intera applicazione. Successivamente il client opera autonomamente, anche in caso di aggiornamento di una pagina nel browser;
- righe 6-18: definiscono l’intestazione HTML <head> delle diverse pagine dell’applicazione:
- riga 7: il tag <title> del titolo delle pagine;
- righe 8-16: i tag <meta>;
- riga 17: i tag <link>
Nell’applicazione generata, il tag <head> è il seguente (codice sorgente della pagina visualizzata nel browser):
<title>nuxt-intro</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="nuxt-intro">
<link data-n-head="ssr" rel="icon" type="image/x-icon" href="/favicon.ico">
<link rel="preload" href="/_nuxt/runtime.js" as="script">
<link rel="preload" href="/_nuxt/commons.app.js" as="script">
<link rel="preload" href="/_nuxt/vendors.app.js" as="script">
<link rel="preload" href="/_nuxt/app.js" as="script">
Ora modifichiamo il file [nuxt.config] nel modo seguente:
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' }]
},
Quando rieseguiamo l’applicazione, il tag <head> è diventato il seguente (codice sorgente della pagina visualizzata nel browser):
<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">
<link rel="preload" href="/_nuxt/runtime.js" as="script">
<link rel="preload" href="/_nuxt/commons.app.js" as="script">
<link rel="preload" href="/_nuxt/vendors.app.js" as="script">
<link rel="preload" href="/_nuxt/app.js" as="script">
Torniamo al file [nuxt.config]:
export default {
mode: 'universal',
/*
** Headers of the page
*/
head: {
...
},
/*
** 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) {}
}
}
- riga 12: tra ogni percorso del client [nuxt], appare una barra di caricamento (loading) se il cambio di percorso richiede un po’ di tempo. La proprietà [loading] consente di configurare questa barra di caricamento, in questo caso il colore della barra;
- riga 16: i file globali [css]. Verranno automaticamente inclusi in tutte le pagine dell’applicazione;
- righe 24-27: i moduli JavaScript necessari per la compilazione (build) dell’applicazione;
- righe 31-36: i moduli JavaScript utilizzati dall’applicazione;
- riga 41: configurazione della libreria [axios] quando questa è stata selezionata dall’utente per le finestre di dialogo HTTP con server di terze parti;
- righe 45-50: configurazione della compilazione (build) del progetto;
È possibile aggiungere altre chiavi al file di configurazione. In particolare, è possibile impostare la porta del servizio (3000 per impostazione predefinita) e la radice del progetto (per impostazione predefinita, la cartella radice del progetto). È proprio ciò che faremo ora aggiungendo le seguenti chiavi:
// directory del codice sorgente
srcDir: '.',
router: {
// URL radice delle pagine dell’applicazione
base: '/nuxt-intro/'
},
// server
server: {
// porta del servizio - predefinita 3000
port: 81,
// indirizzi di rete in ascolto - per impostazione predefinita localhost=127.0.0.1
host: '0.0.0.0'
}
- riga 2: dove trovare il codice sorgente del progetto. Si trova qui nella cartella corrente, ovvero allo stesso livello del file [nuxt.config.js]. È il valore predefinito;
- righe 8-13: configurano il server (non bisogna dimenticare che un’applicazione [nuxt] di tipo [universal] è installata sia su un server che su un browser client di tale server);
- riga 10: le pagine dell’applicazione saranno servite sulla porta 81 del server;
- riga 12: per impostazione predefinita [localhost] (indirizzo di rete 127.0.0.1). Una macchina può avere più indirizzi di rete se appartiene a più reti. L’indirizzo 0.0.0.0 indica che il server web ascolta tutti gli indirizzi di rete della macchina;
- righe 3-6: configurano il router dell’applicazione [nuxt];
- riga 5: le pagine dell’applicazione saranno disponibili su URL e [http://localhost:81/nuxt-intro/];
Aggiungiamo queste righe al file [nuxt.config.js], quindi eseguiamo il progetto (script npm dev). Il risultato è il seguente:

- in [1], l’indirizzo del computer su una rete pubblica;
- in [2], la porta del servizio;
- in [3], la radice dell’applicazione;
3.4. La cartella [layouts]

La cartella [layouts] è destinata ai componenti di impaginazione. Per impostazione predefinita, viene utilizzato il componente denominato [default.vue]. In questo progetto, è il seguente:
<template>
<div>
<nuxt />
</div>
</template>
<style>
html {
font-family: 'Source Sans Pro', -apple-system, BlinkMacSystemFont, 'Segoe UI',
Roboto, 'Helvetica Neue', Arial, sans-serif;
font-size: 16px;
word-spacing: 1px;
-ms-text-size-adjust: 100%;
-webkit-text-size-adjust: 100%;
-moz-osx-font-smoothing: grayscale;
-webkit-font-smoothing: antialiased;
box-sizing: border-box;
}
*,
*:before,
*:after {
box-sizing: border-box;
margin: 0;
}
.button--green {
display: inline-block;
border-radius: 4px;
border: 1px solid #3b8070;
color: #3b8070;
text-decoration: none;
padding: 10px 30px;
}
.button--green:hover {
color: #fff;
background-color: #3b8070;
}
.button--grey {
display: inline-block;
border-radius: 4px;
border: 1px solid #35495e;
color: #35495e;
text-decoration: none;
padding: 10px 30px;
margin-left: 15px;
}
.button--grey:hover {
color: #fff;
background-color: #35495e;
}
</style>
Commenti
- righe 1-5: il componente [template];
- riga 3: il tag <nuxt /> indica la pagina corrente del routing;
- righe 7-55: lo stile incorporato dal componente di layout. Poiché questo contiene la pagina corrente del routing, lo stile verrà applicato a tutte le pagine instradate dell’applicazione;
Si nota che lo scopo principale della pagina [default.vue] è qui quello di applicare uno stile alle pagine instradate.
3.5. La cartella [pages]

La cartella [pages] contiene le viste instradate, ovvero quelle visibili all’utente. La pagina [index.vue] è la pagina iniziale dell’applicazione. Per [nuxt.js] non è presente alcun file di instradamento. I percorsi vengono determinati in base alla struttura della cartella [pages]. In questo caso, la presenza di un file [index.vue] creerà automaticamente un percorso denominato [index] e un percorso [/index] ridotto a [/], poiché si tratta della pagina iniziale. Viene quindi creato il seguente percorso:
Il file [index.vue] è il seguente:
<template>
<div class="container">
<div>
<logo />
<h1 class="title">
nuxt-intro
</h1>
<h2 class="subtitle">
nuxt-intro
</h2>
<div class="links">
<a href="https://nuxtjs.org/" target="_blank" class="button--green">
Documentation
</a>
<a
href="https://github.com/nuxt/nuxt.js"
target="_blank"
class="button--grey"
>
GitHub
</a>
</div>
</div>
</div>
</template>
<script>
import Logo from '~/components/Logo.vue'
export default {
components: {
Logo
}
}
</script>
<style>
.container {
margin: 0 auto;
min-height: 100vh;
display: flex;
justify-content: center;
align-items: center;
text-align: center;
}
.title {
font-family: 'Quicksand', 'Source Sans Pro', -apple-system, BlinkMacSystemFont,
'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
display: block;
font-weight: 300;
font-size: 100px;
color: #35495e;
letter-spacing: 1px;
}
.subtitle {
font-weight: 300;
font-size: 42px;
color: #526488;
word-spacing: 5px;
padding-bottom: 15px;
}
.links {
padding-top: 15px;
}
</style>
Le righe da 1 a 25 del file [template] mostrano la seguente visualizzazione:

L’immagine [1] è generata dalla riga 4 del file [template]. Si nota quindi che la pagina utilizza un componente denominato [logo]. Questo è definito nelle righe 27-35 dello script della pagina. Alla riga 28, la notazione [~] indica la radice del progetto.
3.6. Il componente [Logo]

Il componente [Logo.vue] è il seguente:
<template>
<div class="VueToNuxtLogo">
<div class="Triangle Triangle--two" />
<div class="Triangle Triangle--one" />
<div class="Triangle Triangle--three" />
<div class="Triangle Triangle--four" />
</div>
</template>
<style>
.VueToNuxtLogo {
display: inline-block;
animation: turn 2s linear forwards 1s;
transform: rotateX(180deg);
position: relative;
overflow: hidden;
height: 180px;
width: 245px;
}
.Triangle {
position: absolute;
top: 0;
left: 0;
width: 0;
height: 0;
}
.Triangle--one {
border-left: 105px solid transparent;
border-right: 105px solid transparent;
border-bottom: 180px solid #41b883;
}
.Triangle--two {
top: 30px;
left: 35px;
animation: goright 0.5s linear forwards 3.5s;
border-left: 87.5px solid transparent;
border-right: 87.5px solid transparent;
border-bottom: 150px solid #3b8070;
}
.Triangle--three {
top: 60px;
left: 35px;
animation: goright 0.5s linear forwards 3.5s;
border-left: 70px solid transparent;
border-right: 70px solid transparent;
border-bottom: 120px solid #35495e;
}
.Triangle--four {
top: 120px;
left: 70px;
animation: godown 0.5s linear forwards 3s;
border-left: 35px solid transparent;
border-right: 35px solid transparent;
border-bottom: 60px solid #fff;
}
@keyframes turn {
100% {
transform: rotateX(0deg);
}
}
@keyframes godown {
100% {
top: 180px;
}
}
@keyframes goright {
100% {
left: 70px;
}
}
</style>
Questo componente è costituito essenzialmente da stili e animazioni per creare un'immagine animata.
3.7. Vista DevTools
[Vue DevTools] è l’estensione del browser che consente di ispezionare gli oggetti [nuxt.js] e [vue.js] nel browser. L’abbiamo già utilizzata nel capitolo dedicato a [vue.js]. Esaminiamo cosa rileva questo strumento quando viene visualizzata la pagina iniziale della nostra applicazione:

- in [1], il componente [PagesIndex] indica la pagina [pages/index.vue];
- in [2] si vede che questo componente ha una proprietà [$route] che rappresenta il percorso che ha portato alla pagina [index];
Come semplice esercizio, visualizziamo questo percorso nella console.
3.8. Modifica della pagina iniziale
Modificheremo il file [index.vue]. Nella nostra installazione del progetto, abbiamo installato due dipendenze:
- [eslint]: che verifica la sintassi dei file JavaScript e dei componenti Vue. Se è stata installata l’estensione [ESLint] di VSCode, la sintassi viene verificata durante la digitazione del testo e gli errori vengono segnalati immediatamente;
- [prettier]: che formatta i codici JavaScript in modo standard;
Queste dipendenze sono registrate nel file [package.json]:
"devDependencies": {
"@nuxtjs/eslint-config": "^1.0.1",
"@nuxtjs/eslint-module": "^1.0.0",
"babel-eslint": "^10.0.1",
"eslint": "^6.1.0",
"eslint-config-prettier": "^4.1.0",
"eslint-plugin-nuxt": ">=0.4.2",
"eslint-plugin-prettier": "^3.0.1",
"prettier": "^1.16.4"
}
Ho notato (novembre 2019) che con l’installazione effettuata tramite il comando [yarn create nuxt-app], gli strumenti [eslint, prettier] non funzionano durante la digitazione dei testi. Gli errori vengono segnalati solo in fase di compilazione. Dopo alcune ricerche, ho trovato una configurazione che funziona:

Si installa nella directory principale del progetto una cartella denominata [.vscode] contenente il seguente file [settings.json]:
{
"eslint.validate": [
{
"language": "vue",
"autoFix": true
},
{
"language": "javascript",
"autoFix": true
}
],
"eslint.autoFixOnSave": true,
"editor.formatOnSave": false
}
- righe 2-11: indicano che quando [eslint] convalida i file .vue e .js, deve correggere gli errori che è in grado di correggere;
- riga 12: quando un file viene salvato, [eslint] deve correggere gli errori che è in grado di correggere;
- riga 13: disabilita la formattazione predefinita in VSCode al momento del salvataggio. Sarà [prettier] a occuparsene;
Con questa configurazione:
- gli errori di sintassi o di formattazione vengono segnalati non appena si digita il testo;
- gli errori di formattazione vengono corretti automaticamente al momento del salvataggio del file;
La libreria [prettier] viene configurata dal file [.prettierrc]:

Questo file è, per impostazione predefinita, il seguente:
{
"semi": false,
"arrowParens": "always",
"singleQuote": true
}
- riga 1: nessun punto e virgola alla fine delle istruzioni;
- riga 2: se una funzione «freccia» (arrow) ha un unico parametro, questo è racchiuso tra parentesi;
- riga 3: le stringhe di caratteri sono racchiuse tra apostrofi (non tra virgolette);
Aggiungiamo le due regole seguenti:
{
"semi": false,
"arrowParens": "always",
"singleQuote": true,
"printWidth": 120,
"endOfLine": "auto"
}
- riga 5: la riga di codice può contenere fino a 120 caratteri;
- riga 6: il carattere di fine riga può essere indifferentemente CRLF (Windows) o LF (Unix);
Infine, il file [package.json] viene modificato come segue:
"scripts": {
"dev": "nuxt",
"build": "nuxt build",
"start": "nuxt start",
"generate": "nuxt generate",
"lint": "eslint --ext .js,.vue --ignore-path .gitignore .",
"lintfix": "eslint --fix --ext .js,.vue --ignore-path .gitignore ."
},
- riga 7: aggiungiamo il comando [lintfix], che è identico al comando [lint] della riga 6, tranne per il fatto che include in più il parametro [--fix]. Il comando [lint] verifica la sintassi e il formato di tutti i file del progetto e segnala eventuali errori. [lintfix] farà la stessa cosa, con la differenza che i problemi di formattazione che possono essere corretti verranno risolti automaticamente. [lintfix] sarà il comando da utilizzare se la compilazione fallisce a causa di problemi di formattazione dei file;
Fatto ciò, modifichiamo il file [index.vue] nel modo seguente:

<script>
/* eslint-disable no-console */
import Logo from '~/components/Logo.vue'
export default {
components: {
Logo
},
// ciclo di vita
created() {
console.log('created, route=', this.$route)
}
}
</script>
- righe 10-12: si aggiunge la funzione [created], che viene eseguita automaticamente una volta creato il componente;
- riga 11: si visualizza il percorso corrente;
- riga 2: un commento destinato a [eslint]. Senza questo commento, [eslint] segnala un errore; riga 11: non ammette istruzioni [console] nelle funzioni del ciclo di vita. [eslint] è configurabile. Manterremo la sua configurazione predefinita e utilizzeremo commenti come quello della riga 2 per disattivare una regola specifica di [eslint]. Utilizzeremo due tipi di commenti:
- /* disattivazione della regola [eslint] */: disattivazione di una regola per l’intero file;
- // disattivazione della regola [eslint]: disattivazione di una regola per la riga successiva;
Durante la digitazione, gli errori vengono segnalati ed è disponibile una funzione [Quick Fix]:

Si esegue il progetto:

- in [1], la scheda [Vue] degli strumenti di sviluppo del browser (F12);
- in [2] e [3], la visualizzazione del percorso;
Perché due visualizzazioni e non una sola?
Un’applicazione [nuxt] si compone di due elementi, un server e un client:
- il server fornisce le pagine dell’applicazione all’avvio della stessa e poi ogni volta che una pagina viene aggiornata nel browser (F5) oppure quando l’utente digita manualmente un URL dell’applicazione;
- ogni pagina fornita dal browser contiene la pagina richiesta e il codice JavaScript dell’intera applicazione, che viene poi eseguito nel browser. Questo è il client. Finché non viene effettuato un aggiornamento della pagina nel browser, l’applicazione funziona come una classica applicazione Vue in modalità [sap] (Single Page Application). Non appena l’utente provoca manualmente un aggiornamento della pagina, questa viene richiesta al server e si ritorna alla fase 1 precedente.
Ciò che occorre comprendere è che si tratta delle stesse pagine della cartella [pages] fornite sia dal server che dal client. Per questo motivo, gli sviluppatori di [nuxt] definiscono questo tipo di pagine “pagine isomorfe”. Le stesse pagine [.vue] possono essere interpretate sia dal client che dal server. Prendiamo ad esempio la pagina [index]:
<template>
<div class="container">
<div>
<logo />
<h1 class="title">
nuxt-intro
</h1>
<h2 class="subtitle">
nuxt-intro
</h2>
<div class="links">
<a href="https://nuxtjs.org/" target="_blank" class="button--green">
Documentation
</a>
<a
href="https://github.com/nuxt/nuxt.js"
target="_blank"
class="button--grey"
>
GitHub
</a>
</div>
</div>
</div>
</template>
<script>
/* eslint-disable no-console */
import Logo from '~/components/Logo.vue'
export default {
components: {
Logo
},
// ciclo di vita
created() {
console.log('created, route=', this.$route)
}
}
</script>
Trattandosi della pagina iniziale, all’avvio dell’applicazione viene fornita dal server. Anche la pagina sul server ha un ciclo di vita, identico a quello di una pagina [Vue] classica, tranne che per le funzioni [beforeMount, monted] che non esistono sul lato server. La funzione [created] viene eseguita, il che spiega il primo log. Ciò significa, tra l’altro, che il server è in grado di eseguire script JavaScript. In questo caso, e in generale, si tratta di un server [node.js]. Una volta creata la pagina sul server, questa arriva al browser dove viene nuovamente sottoposta al ciclo di vita. La funzione [created] viene eseguita una seconda volta, il che genera il secondo log.
L’architettura di un’applicazione [nuxt] potrebbe essere la seguente:

- [1]: il browser che ospita l’applicazione [nuxt] una volta che questa è stata caricata sul browser. È ciò che viene definito il client [nuxt];
- [3]: il server che ospita inizialmente l’applicazione [nuxt]. Quest’ultima viene caricata sul browser [1] all’avvio dell’applicazione e ogni volta che l’utente aggiorna la pagina corrente del browser o digita manualmente un URL dell’applicazione. È qui che risiede la differenza di funzionamento rispetto a un’applicazione Vue classica. Con quest’ultima, una volta caricata sul browser, il server non veniva più interpellato in seguito. Un’altra differenza importante che per ora non abbiamo potuto osservare è che il server di un’applicazione Vue è un server statico, incapace di interpretare le pagine [.vue], mentre quello di un’applicazione Nuxt di tipo [universal] è un server JavaScript. Prima di inviare una pagina al browser, il server può eseguire script e, ad esempio, recuperare dati dal server [2];
- [2]: è il server che fornisce dati sia ai client [nuxt] e [1], sia ai server [nuxt] e [3];
Nello schema sopra riportato si possono distinguere tre sottosistemi client/server:
- [1, 3]: ospita l’applicazione [nuxt]. [3] la fornisce all’avvio dell’applicazione con la pagina iniziale e ogni volta che l’utente richiede manualmente una pagina. [1] ospita l’’applicazione [nuxt] ricevuta da [3], che a sua volta funziona in modalità [SAP] finché le pagine non vengono richieste manualmente a [3];
- [1, 2]: in modalità [SAP], il client [nuxt] recupera dati esterni da uno o più server;
- [3, 2]: durante la generazione della pagina richiesta dall’utente, anche il server [3] può recuperare dati esterni da uno o più server;
È quindi il server [3] a distinguere un’applicazione [nuxt] da un’applicazione [vue]. Questo server viene interpellato ogni volta che l’utente richiede manualmente una pagina. Esso elabora le stesse pagine [.vue] del client [vue] e [1]. Si tratta di un server JavaScript in grado di eseguire gli script presenti nella pagina. Ciò può modificare, ad esempio, il modo in cui viene generata la pagina iniziale con dati esterni: mentre un’applicazione [vue] ottiene tali dati necessariamente dal client [1], qui possono essere recuperati dal server [3] prima che la pagina venga inviata al client. La pagina iniziale acquista così rilevanza e può contribuire a migliorare il SEO dell’applicazione.
Nota: in modalità sviluppo, le tre entità [1, 2, 3] si trovano spesso sulla stessa macchina. Questo sarà il caso in tutti i nostri esempi.
3.9. Spostamento del codice sorgente dell’applicazione in una cartella separata
Successivamente, creeremo diverse applicazioni [nuxt] nella stessa cartella [dvp]. Infatti, la cartella delle dipendenze [node_modules] generata per ogni progetto [nuxt] può raggiungere diverse centinaia di megabyte. Creeremo diverse cartelle [nuxt-00, nuxt-01, ...] all’interno della cartella [dvp] per contenere il codice sorgente degli esempi da testare. Successivamente utilizzeremo il file di configurazione [nuxt-config.js] per indicare dove si trova il codice sorgente del progetto [dvp], che rimarrà l’unico progetto [nuxt] di questo tutorial.
Spostiamo il codice sorgente dell’applicazione generata inizialmente dal comando [yarn create nuxt-app] in una cartella [nuxt-00]:

- in [2], abbiamo spostato le cartelle [components, layouts, pages] in una cartella denominata [nuxt-00];
- in [3], dobbiamo modificare il file [nuxt.config.js];
Modifichiamo il file [nuxt.config.js] come segue:
export default {
mode: 'universal',
/*
** Headers of the page
*/
...
/*
** Build configuration
*/
build: {
/*
** You can extend webpack config here
*/
extend(config, ctx) {}
},
// directory del codice sorgente
srcDir: 'nuxt-00',
// router
router: {
// radice dei URL dell'applicazione
base: '/nuxt-00/'
},
// server
server: {
// porta di servizio, 3000 per impostazione predefinita
port: 81,
// indirizzi di rete in ascolto, per impostazione predefinita localhost: 127.0.0.1
// 0.0.0.0 = tutti gli indirizzi di rete del computer
host: '0.0.0.0'
}
}
Il file viene modificato in due punti:
- riga 17: si indica che il codice sorgente del progetto [dvp] si trova nella cartella [nuxt-00];
- riga 21: si indica che la radice dell’applicazione URL è ora [/nuxt-00/]. Questa modifica non era obbligatoria. Si potrebbe omettere questa proprietà e la radice di URL sarebbe quindi [/]. In questo caso, ciò ci consentirà di ricordare che il codice sorgente eseguito è quello della cartella [nuxt-00];
Fatto ciò, il progetto [dvp] viene eseguito come in precedenza:

3.10. Distribuzione dell’applicazione [nuxt-00]
Eseguiremo l’applicazione [nuxt-00] in un ambiente diverso da quello integrato di VSCode.
Per prima cosa compiliamo l’applicazione:

- in [3], il risultato della compilazione del client. Verrà eseguito dal browser;
- in [4], il risultato della compilazione del server. Verrà eseguito dal server [node.js];
Il risultato della compilazione viene inserito nella cartella [.nuxt]:

Copiamo le cartelle [.nuxt, node_modules] e i file [package.json, nuxt.config.js] in una cartella separata:

Il file [package.json] viene semplificato come segue:
{
"scripts": {
"start": "nuxt start"
}
}
- si mantiene solo lo script [start] che consente di eseguire la versione compilata del progetto;
Il file [nuxt.config.js] viene semplificato come segue:
export default {
// router
router: {
// radice dei URL dell'applicazione
base: '/nuxt-00/'
},
// server
server: {
// porta di servizio, 3000 per impostazione predefinita
port: 81,
// indirizzi di rete in ascolto, per impostazione predefinita localhost: 127.0.0.1
// 0.0.0.0 = tutti gli indirizzi di rete del computer
host: '0.0.0.0'
}
}
- riga 5: si imposta l'URL di base dell'applicazione compilata;
- righe 8-14: si definiscono la porta di servizio e gli indirizzi di rete in ascolto;
Fatto ciò, si apre un terminale Laragon e ci si posiziona nella cartella contenente la versione compilata del progetto. È possibile aprire qualsiasi tipo di terminale, ma è necessario che l’eseguibile [npm] si trovi nella directory del terminale. Questo è il caso del terminale Laragon.
A questo punto, digitare il comando [npm run start]:

In [3] si vede che è stato avviato un server e che è in ascolto su URL [http://192.168.1.128:81/nuxt-00/]. Ora proviamo a richiedere questo URL con un browser [4]. Otteniamo esattamente lo stesso risultato di prima. Sul terminale sono stati scritti dei log [5]. Si tratta del log inserito nel metodo [created] della pagina [index.vue] che è stata eseguita dal server [node.js].

Sul lato browser [6], si trova anche il log del metodo [created] della pagina [index.vue], ma questa volta eseguito dal client.
3.11. Configurazione di un server sicuro
Come indicato sopra, l’URL dell’applicazione è [http://192.168.1.128/nuxt-00/]. Vorremmo che fosse [https://192.168.1.128/nuxt-00/]. Dobbiamo quindi creare un server sicuro. Vi mostriamo come procedere.
Nota: il metodo è stato tratto dall’articolo [https://stackoverflow.com/questions/56966137/how-to-run-nuxt-npm-run-dev-with-https-in-localhost].
Per prima cosa creiamo una chiave privata e una chiave pubblica con [openssl]. [openssl] viene normalmente installato insieme al server Laragon. Di conseguenza, questo comando è disponibile in qualsiasi terminale Laragon. Apriamo quindi un terminale Laragon e posizioniamoci nella cartella dell’applicazione distribuita:


- in [2], si digita il comando [openssl genrsa 2048 > server.key];
- in [3], viene creato un file [server.key];
- in [4], si digita il comando [openssl req -new -x509 -nodes -sha256 -days 365 -key server.key -out server.crt];
- in [5], viene creato un file [server.crt];
Questi due file costituiscono un certificato autofirmato. La maggior parte dei browser li accetta solo previa approvazione dell’utente che ha richiesto la pagina.
I file [server.key, server.crt] devono ora essere utilizzati dall’applicazione web. A tal fine, il file [nuxt.config.js] deve essere modificato come segue:
import path from 'path'
import fs from 'fs'
export default {
// router
router: {
// radice dei URL dell'applicazione
base: '/nuxt-00/'
},
// server
server: {
// porta di servizio, 3000 per impostazione predefinita
port: 81,
// indirizzi di rete in ascolto, per impostazione predefinita localhost: 127.0.0.1
// 0.0.0.0 = tutti gli indirizzi di rete del computer
host: '0.0.0.0',
// certificato autofirmato
https: {
key: fs.readFileSync(path.resolve(__dirname, 'server.key')),
cert: fs.readFileSync(path.resolve(__dirname, 'server.crt'))
}
}
}
Sono le righe 18-21 che implementano il protocollo [https].
Ora rieseguiamo l’applicazione:

3.12. Fine del primo esempio
Il primo esempio è ora terminato. Ci ha permesso di apprendere molti concetti relativi a [nuxt]. Ora svilupperemo altri esempi che inseriremo nelle cartelle [nuxt-01, nuxt-02, ...]. Poiché questi esempi utilizzeranno un file [nuxt.config.js] diverso, salveremo in ciascuna di queste cartelle il file [nuxt.config.js] che è servito per eseguirli:
