Creazione di app Vue.js renderizzate lato server utilizzando Nuxt.js
Pubblicato: 2022-03-11I framework/librerie JavaScript come Vue possono offrire una fantastica esperienza utente durante la navigazione nel tuo sito. La maggior parte offre un modo per modificare dinamicamente il contenuto della pagina senza dover inviare ogni volta una richiesta al server.
Tuttavia, c'è un problema con questo approccio. Quando carichi inizialmente il tuo sito web, il tuo browser non riceve una pagina completa da visualizzare. Invece, riceve un mucchio di pezzi per costruire la pagina (HTML, CSS, altri file) e le istruzioni su come metterli tutti insieme (un framework/libreria JavaScript) Ci vuole una quantità di tempo misurabile per mettere insieme tutte queste informazioni prima che il tuo browser abbia effettivamente qualcosa da visualizzare. È come ricevere un mucchio di libri insieme a una libreria piatta. Dovresti prima costruire la libreria e poi riempirla di libri.
La soluzione a questo è intelligente: disporre di una versione del framework/libreria sul server in grado di creare una pagina pronta per la visualizzazione. Quindi invia questa pagina completa al browser insieme alla possibilità di apportare ulteriori modifiche e di avere ancora il contenuto dinamico della pagina (il framework/libreria), proprio come viene inviata una libreria già pronta insieme ad alcuni libri. Certo, devi ancora mettere i libri nella libreria, ma hai qualcosa di utilizzabile subito.
Al di là della sciocca analogia, ci sono anche un sacco di altri vantaggi. Ad esempio, una pagina che cambia raramente, come una pagina Chi siamo, non ha bisogno di essere ricreata ogni volta che un utente lo richiede. Quindi un server può crearlo una volta e quindi memorizzarlo nella cache o archiviarlo da qualche parte per un uso futuro. Questi tipi di miglioramenti della velocità possono sembrare minimi, ma in un ambiente in cui il tempo necessario alla reattività viene misurato in millisecondi (o meno), ogni piccolo bit conta.
Se desideri maggiori informazioni sui vantaggi di SSR in un ambiente Vue, dovresti consultare l'articolo di Vue su SSR. C'è una varietà di opzioni per ottenere questi risultati, ma la più popolare, consigliata anche dal team Vue, è Nuxt.
Perché Nuxt.js
Nuxt.js si basa su un'implementazione di SSR per la popolare libreria React chiamata Next. Dopo aver visto i vantaggi di questo design, è stata progettata un'implementazione simile per Vue chiamata Nuxt. Coloro che hanno familiarità con la combinazione React+Next noteranno un sacco di somiglianze nel design e nel layout dell'applicazione. Tuttavia, Nuxt offre funzionalità specifiche di Vue per creare una soluzione SSR potente ma flessibile per Vue.
Nuxt è stato aggiornato a una versione 1.0 pronta per la produzione a gennaio 2018 e fa parte di una comunità attiva e ben supportata. Una delle cose grandiose è che la creazione di un progetto utilizzando Nuxt non è molto diversa dalla creazione di qualsiasi altro progetto Vue. In effetti, fornisce una serie di funzionalità che ti consentono di creare basi di codice ben strutturate in un lasso di tempo ridotto.
Un'altra cosa importante da notare è che Nuxt non deve essere usato per SSR . È promosso come framework per la creazione di applicazioni Vue.js universali e include un comando ( nuxt generate
) per la creazione di applicazioni Vue generate statiche utilizzando la stessa base di codice. Quindi, se sei preoccupato di immergerti in profondità nell'SSR, non farti prendere dal panico. Puoi sempre creare un sito statico, pur continuando a sfruttare le funzionalità di Nuxt.
Per cogliere le potenzialità di Nuxt, creiamo un semplice progetto. Il codice sorgente finale per questo progetto è ospitato su GitHub se vuoi vederlo, oppure puoi visualizzare una versione live creata usando nuxt generate
e ospitata su Netlify.
Creazione di un progetto Nuxt
Per iniziare, utilizziamo un generatore di progetti Vue chiamato vue-cli
per creare rapidamente un progetto di esempio:
# install vue-cli globally npm install -g vue-cli # create a project using a nuxt template vue init nuxt-community/starter-template my-nuxt-project
Dopo aver esaminato un paio di opzioni, questo creerà un progetto all'interno della cartella my-nuxt-project
o qualunque cosa tu abbia specificato. Quindi dobbiamo solo installare le dipendenze ed eseguire il server:
cd my-nuxt-project npm install # Or yarn npm run dev
Eccoci. Apri il tuo browser su localhost:3000
e il tuo progetto dovrebbe essere in esecuzione. Non molto diverso dalla creazione di un progetto Vue Webpack. Tuttavia, quando osserviamo la struttura effettiva dell'app, non c'è molto, soprattutto se confrontata con qualcosa come il modello Vue Webpack.
L'analisi di package.json
mostra anche che abbiamo solo una dipendenza, Nuxt stesso. Questo perché ogni versione di Nuxt è su misura per funzionare con versioni specifiche di Vue, Vue-router e Vuex e li raggruppa tutti insieme per te.
C'è anche un file nuxt.config.js
nella radice del progetto. Ciò ti consente di personalizzare un sacco di funzionalità fornite da Nuxt. Per impostazione predefinita, imposta i tag di intestazione, il colore della barra di caricamento e le regole ESLint per te. Se sei ansioso di vedere cosa puoi configurare, ecco la documentazione; tratteremo alcune opzioni in questo articolo.
Allora, cosa c'è di così speciale in quelle directory?
Disposizione del progetto
Se sfogli le directory create, tutte hanno un file Leggimi di accompagnamento che indica un breve riassunto di ciò che va in quella directory e spesso un collegamento ai documenti.
Questo è uno dei vantaggi dell'utilizzo di Nuxt: una struttura predefinita per la tua applicazione. Qualsiasi buon sviluppatore front-end strutturerà un'applicazione simile a questa, ma ci sono molte idee diverse sulle strutture e quando si lavora in un team, un po' di tempo passerà inevitabilmente alla discussione o alla scelta di questa struttura. Nuxt ne fornisce uno per te.
Nuxt cercherà determinate directory e creerà la tua applicazione per te a seconda di ciò che trova. Esaminiamo queste directory una per una.
Pagine
Questa è l'unica directory richiesta . Tutti i componenti Vue in questa directory vengono automaticamente aggiunti a vue-router
in base ai nomi dei file e alla struttura della directory. Questo è estremamente conveniente. Normalmente avrei comunque una directory di Pages separata e dovrei registrare manualmente ciascuno di questi componenti in un altro file del router. Questo file router può diventare complesso per progetti più grandi e potrebbe essere necessario dividerlo per mantenere la leggibilità. Invece, Nuxt gestirà tutta questa logica per te.
Per dimostrare, possiamo creare un componente Vue chiamato about.vue
all'interno della directory Pages. Aggiungiamo semplicemente un modello semplice come:
<template> <h1>About Page</h1> </template>
Quando salvi, Nuxt rigenererà i percorsi per te. Dato che abbiamo chiamato il nostro componente about.vue
, se vai a /about
, dovresti vedere quel componente. Semplice.
C'è un nome di file che è speciale. Assegnare un nome a un file index.vue
creerà una route principale per quella directory. Quando il progetto viene generato, c'è già un componente index.vue
nella directory delle pagine che è correlato alla home page o alla pagina di destinazione del tuo sito. (Nell'esempio di sviluppo, questo sarebbe semplicemente localhost:3000
.)
E i percorsi più profondi? Le sottodirectory nella directory di Pages aiutano a strutturare i tuoi percorsi. Quindi, se volessimo una pagina Visualizza prodotto, potremmo strutturare la nostra directory delle pagine in questo modo:
/pages --| /products ----| index.vue ----| view.vue
Ora, se andiamo a /products/view
, vedremo il componente view.vue
all'interno della directory dei prodotti. Se andiamo invece a /products
, vedremo il componente index.vue
all'interno della directory products.
Forse ti starai chiedendo perché non abbiamo semplicemente creato un componente products.vue
nella directory pages come abbiamo fatto per la pagina /about
. Potresti pensare che il risultato sarebbe lo stesso, ma c'è una differenza tra le due strutture. Dimostriamolo aggiungendo un'altra nuova pagina.
Supponiamo di volere una pagina Informazioni separata per ogni dipendente. Ad esempio, creiamo una pagina Informazioni per me. Dovrebbe trovarsi in /about/ben-jones
. Inizialmente, potremmo provare a strutturare la directory di Pages in questo modo:
/pages --| about.vue --| /about ----| ben-jones.vue
Quando proviamo ad accedere a /about/ben-jones
, otteniamo invece il componente about.vue
, lo stesso di /about
. Cosa sta succedendo qui?
È interessante notare che ciò che Nuxt sta facendo qui è generare un percorso annidato . Questa struttura suggerisce che si desidera un percorso permanente /about
e qualsiasi cosa all'interno di tale percorso dovrebbe essere nidificato nella propria area di visualizzazione. In vue-router, questo sarebbe indicato specificando un componente <router-view />
all'interno del componente about.vue
. In Nuxt, questo è lo stesso concetto tranne che, invece di <router-view />
, usiamo semplicemente <nuxt />
. Quindi aggiorniamo il nostro componente about.vue
per consentire percorsi nidificati:
<template> <div> <h1>About Page</h1> <nuxt /> </div> </template>
Ora, quando andiamo a /about
, otteniamo il componente about.vue
che avevamo prima, con solo un titolo. Tuttavia, quando andiamo a /about/ben-jones
, abbiamo invece il titolo e il componente ben-jones.vue
renderizzati dove si trovava il segnaposto <nuxt/>
.
Questo non era quello che volevamo inizialmente, ma l'idea di avere una pagina Informazioni con un elenco di persone che, una volta cliccate, riempiono una sezione della pagina con le loro informazioni è un concetto interessante, quindi lasciamo perdere per ora . Se volessi l'altra opzione, tutto ciò che faremmo è ristrutturare le nostre directory. Dovremmo solo spostare il componente about.vue
all'interno della directory /about
e rinominarlo index.vue
, quindi la struttura risultante sarebbe:
/pages --| /about ----| index.vue ----| ben-jones.vue
Infine, supponiamo di voler utilizzare i parametri di percorso per recuperare un prodotto specifico. Ad esempio, vogliamo essere in grado di modificare un prodotto accedendo a /products/edit/64
dove 64 è product_id
. Possiamo farlo nel modo seguente:
/pages --| /products ----| /edit ------| _product_id.vue
Nota il carattere di sottolineatura all'inizio del componente _product_id.vue
: questo indica un parametro di percorso che è quindi accessibile $route.params
o sull'oggetto params
nel Contesto di Nuxt (ne parleremo più avanti). Nota che la chiave per il parametro sarà il nome del componente senza il carattere di sottolineatura iniziale, in questo caso product_id
, quindi cerca di mantenerli univoci all'interno del progetto. Di conseguenza, in _product_id.vue
, potremmo avere qualcosa come:
<template> <h1>Editing Product {{ $route.params.product_id }}</h1> </template>
Puoi iniziare a immaginare layout più complessi, che sarebbe difficile da configurare utilizzando vue-router. Ad esempio, possiamo combinare tutto quanto sopra in un percorso come:
/pages --| /categories ----| /_category_id ------| products.vue ------| /products --------| _product_id.vue
Non è troppo difficile ragionare su cosa visualizzerebbe /categories/2/products/3
. Avremmo il componente products.vue
con un componente _product_id.vue
annidato , con due parametri di percorso: category_id
e product_id
. Questo è molto più semplice su cui ragionare rispetto a una configurazione del router equivalente.
Mentre siamo sull'argomento, una cosa che tendo a fare nella configurazione del router è impostare le protezioni del router. Poiché Nuxt sta costruendo il router per noi, questo può essere fatto invece sul componente stesso con beforeRouteEnter
. Se vuoi convalidare i parametri di percorso, Nuxt fornisce un metodo componente chiamato validate
. Quindi, se si desidera verificare se product_id
era un numero prima di provare a eseguire il rendering del componente, è necessario aggiungere quanto segue al tag di script di _product_id.vue
:
export default { validate ({ params }) { // Must be a number return /^\d+$/.test(params.product_id) } }
Ora, passando a /categories/2/products/someproduct
ottiene un 404 perché someproduct
non è un numero valido.
Questo è tutto per la directory di Pages. Imparare a strutturare correttamente i tuoi percorsi in questa directory è essenziale, quindi dedicare un po' di tempo all'inizio è importante per ottenere il massimo da Nuxt. Se stai cercando una breve panoramica, è sempre utile fare riferimento ai documenti per il routing.
Se sei preoccupato di non avere il controllo del router, non esserlo. Questa configurazione predefinita funziona alla grande per un'ampia varietà di progetti, a condizione che siano ben strutturati. Tuttavia, ci sono alcuni casi in cui potrebbe essere necessario aggiungere più percorsi al router di quelli che Nuxt genera automaticamente per te o ristrutturarli. Nuxt fornisce un modo per personalizzare l'istanza del router nella configurazione, consentendoti di aggiungere nuovi percorsi e personalizzare i percorsi generati. Puoi anche modificare le funzionalità principali dell'istanza del router, incluse le opzioni extra aggiunte da Nuxt. Quindi, se incontri un caso limite, hai ancora la flessibilità per trovare la soluzione appropriata.
Negozio
Nuxt può creare il tuo negozio Vuex in base alla struttura della directory del negozio, simile alla directory di Pages. Se non hai bisogno di un negozio, rimuovi semplicemente la directory. Ci sono due modalità per il negozio, Classic e Moduli.
Classic richiede che tu abbia un file index.js
nella directory del negozio. Lì devi esportare una funzione che restituisce un'istanza Vuex:
import Vuex from 'vuex' const createStore = () => { return new Vuex.Store({ state: ..., mutations: ..., actions: ... }) } export default createStore
Ciò ti consente di creare il negozio come desideri, proprio come usare Vuex in un normale progetto Vue.
La modalità Moduli richiede anche la creazione di un file index.js
nella directory del negozio. Tuttavia, questo file deve solo esportare lo stato principale/mutazioni/azioni per il tuo negozio Vuex. L'esempio seguente specifica uno stato radice vuoto:
export const state = () => ({})
Quindi, ogni file nella directory del negozio verrà aggiunto al negozio nel proprio spazio dei nomi o modulo. Ad esempio, creiamo un punto in cui archiviare il prodotto corrente. Se creiamo un file chiamato product.js
nella directory del negozio, una sezione del negozio con spazio dei nomi sarà disponibile in $store.product
. Ecco un semplice esempio di come potrebbe apparire quel file:
export const state = () => ({ _id: 0, title: 'Unknown', price: 0 }) export const actions = { load ({ commit }) { setTimeout( commit, 1000, 'update', { _id: 1, title: 'Product', price: 99.99 } ) } } export const mutations = { update (state, product) { Object.assign(state, product) } }
Il setTimeout
nell'azione di caricamento simula una sorta di chiamata API, che aggiornerà l'archivio con la risposta; in questo caso, ci vuole un secondo. Ora, usiamolo nella pagina products/view
:
<template> <div> <h1>View Product {{ product._id }}</h1> <p>{{ product.title }}</p> <p>Price: {{ product.price }}</p> </div> </template> <script> import { mapState } from 'vuex' export default { created () { this.$store.dispatch('product/load') }, computed: { ...mapState(['product']) } } </script>
Alcune cose da notare: qui, chiamiamo la nostra falsa API quando viene creato il componente. Puoi vedere che l'azione product/load
che stiamo inviando ha uno spazio dei nomi in Prodotto. Questo rende chiaro esattamente con quale sezione del negozio abbiamo a che fare. Quindi, mappando lo stato su una proprietà calcolata locale, possiamo facilmente utilizzarlo nel nostro modello.
C'è un problema: vediamo lo stato originale per un secondo mentre l'API è in esecuzione. Successivamente, utilizzeremo una soluzione fornita da Nuxt per risolvere questo problema (noto come fetch
).
Tanto per sottolineare questo ancora una volta, non abbiamo mai dovuto npm install vuex
, poiché è già incluso nel pacchetto Nuxt. Quando aggiungi un file index.js
alla directory del negozio, tutti questi metodi vengono aperti automaticamente .
Queste sono le due directory principali spiegate; il resto è molto più semplice.
Componenti
La directory Components contiene i tuoi componenti riutilizzabili come una barra di navigazione, una galleria di immagini, un'impaginazione, tabelle di dati, ecc. Visto che i componenti nella directory di Pages vengono convertiti in percorsi, è necessario un altro posto per archiviare questi tipi di componenti. Questi componenti sono accessibili nelle pagine o in altri componenti importandoli:
import ComponentName from ~/components/ComponentName.vue
Risorse
Questo contiene risorse non compilate e ha più a che fare con il modo in cui Webpack carica ed elabora i file, piuttosto che con il funzionamento di Nuxt. Se sei interessato, ti consiglio di leggere la guida nel Readme.
Statico
Questo contiene file statici che sono mappati alla directory principale del tuo sito. Ad esempio, inserire un'immagine chiamata logo.png in questa directory la renderebbe disponibile in /logo.png
. Questo è utile per i meta file come robots.txt, favicon.ico e altri file necessari.
Layout
Normalmente, in un progetto Vue, hai una sorta di componente radice, normalmente chiamato App.vue
. Qui è dove puoi impostare il layout dell'app (normalmente statico), che può includere una barra di navigazione, un piè di pagina e quindi un'area di contenuto per il tuo vue-router. Il layout default
fa esattamente questo ed è fornito nella cartella dei layout. Inizialmente, tutto ciò che ha è un div con un componente <nuxt />
(che equivale a <router-view />
) ma può essere modellato come preferisci. Ad esempio, ho aggiunto una semplice barra di navigazione al progetto di esempio per la navigazione tra le varie pagine dimostrative.

Potresti voler avere un layout diverso per una determinata sezione della tua app. Forse hai una sorta di CMS o pannello di amministrazione che sembra diverso. Per risolvere questo problema, crea un nuovo layout nella directory Layouts. Ad esempio, creiamo un layout admin-layout.vue
che ha solo un tag di intestazione aggiuntivo e nessuna barra di navigazione:
<template> <div> <h1>Admin Layout</h1> <nuxt /> </div> </template>
Quindi, possiamo creare una pagina admin.vue
nella directory Pages e utilizzare una proprietà fornita da Nuxt chiamata layout
per specificare il nome (come stringa) del layout che vogliamo utilizzare per quel componente:
<template> <h1>Admin Page</h1> </template> <script> export default { layout: 'admin-layout' } </script>
Questo è tutto ciò che c'è da fare. I componenti della pagina utilizzeranno il layout default
, a meno che non sia specificato, ma quando accedi a /admin
, ora utilizza il layout admin-layout.vue
. Ovviamente, questo layout può essere condiviso su più schermate di amministrazione, se lo desideri. L'unica cosa importante da ricordare è che i layout devono contenere un elemento <nuxt />
.
C'è un'ultima cosa da notare sui layout. Potresti aver notato durante la sperimentazione che se digiti un URL non valido, ti viene mostrata una pagina di errore. Questa pagina di errore è, infatti, un altro layout. Nuxt ha il proprio layout di errore (codice sorgente qui), ma se si desidera modificarlo, è sufficiente creare un layout error.vue
e verrà utilizzato al suo posto. L'avvertenza qui è che il layout dell'errore non deve avere un elemento <nuxt />
. Avrai anche accesso a un oggetto di error
sul componente con alcune informazioni di base da visualizzare. (Questo viene stampato nel terminale che esegue Nuxt se si desidera esaminarlo.)
Middleware
I middleware sono funzioni che possono essere eseguite prima del rendering di una pagina o di un layout. Ci sono una serie di motivi per cui potresti volerlo fare. La protezione del percorso è un uso popolare in cui è possibile controllare il negozio Vuex per un accesso valido o convalidare alcuni parametri (invece di utilizzare il metodo di validate
sul componente stesso). Un progetto su cui ho lavorato sul middleware utilizzato di recente per generare breadcrumb dinamici in base al percorso e ai parametri.
Queste funzioni possono essere asincrone; fai solo attenzione, poiché nulla verrà mostrato all'utente fino a quando il middleware non sarà risolto. Hanno anche accesso al contesto di Nuxt, che spiegherò in seguito.
Plugin
Questa directory ti consente di registrare i plugin Vue prima che l'applicazione venga creata. Ciò consente al plug-in di essere condiviso in tutta la tua app sull'istanza Vue e di essere accessibile in qualsiasi componente.
La maggior parte dei principali plugin ha una versione Nuxt che può essere facilmente registrata nell'istanza Vue seguendo i loro documenti. Tuttavia, ci saranno circostanze in cui svilupperai un plug-in o dovrai adattare un plug-in esistente a questo scopo. Un esempio che sto prendendo in prestito dai documenti mostra come farlo per vue-notifications
. Per prima cosa, dobbiamo installare il pacchetto:
npm install vue-notifications --save
Quindi crea un file nella directory dei plugin chiamato vue-notifications.js
e includi quanto segue:
import Vue from 'vue' import VueNotifications from 'vue-notifications' Vue.use(VueNotifications)
Molto simile a come registreresti un plugin in un normale ambiente Vue. Quindi modifica il file nuxt.config.js
nella radice del tuo progetto e aggiungi la seguente voce all'oggetto module.exports:
plugins: ['~/plugins/vue-notifications']
Questo è tutto. Ora puoi utilizzare vue-notifications
tutta la tua app. Un esempio di questo è in /plugin
nel progetto di esempio.
In modo che completi un riepilogo della struttura della directory. Può sembrare molto da imparare, ma se stai sviluppando un'app Vue, stai già impostando lo stesso tipo di logica. Nuxt aiuta ad astrarre la configurazione e ti aiuta a concentrarti sulla costruzione.
Nuxt fa molto di più che assistere nello sviluppo. Potenzia i tuoi componenti fornendo funzionalità extra.
I componenti sovralimentati di Nuxt
Quando ho iniziato a fare ricerche su Nuxt, ho continuato a leggere come vengono sovralimentati i componenti di Page . Suonava alla grande, ma non era immediatamente ovvio cosa significasse esattamente e quali vantaggi portasse.
Ciò significa che tutti i componenti della Pagina hanno metodi aggiuntivi collegati che Nuxt può utilizzare per fornire funzionalità aggiuntive. In effetti, ne abbiamo già visto uno in precedenza quando abbiamo utilizzato il metodo validate
per controllare i parametri e reindirizzare un utente se non sono validi.
I due principali utilizzati in un progetto Nuxt saranno i metodi asyncData
e fetch
. Entrambi sono molto simili nel concetto, vengono eseguiti in modo asincrono prima della generazione del componente e possono essere utilizzati per popolare i dati di un componente e dell'archivio. Consentono inoltre il rendering completo della pagina sul server prima di inviarla al client anche quando dobbiamo attendere una chiamata al database o all'API.
Qual è la differenza tra asyncData
e fetch
?
-
asyncData
viene utilizzato per popolare i dati del componente Pagina. Quando restituisci un oggetto, viene quindi unito all'output deidata
prima del rendering. -
fetch
viene utilizzato per popolare il Vuex Store. Se restituisci una promessa, Nuxt attenderà che venga risolta prima di eseguire il rendering.
Quindi mettiamoli a frutto. Ricordi in precedenza nella pagina /products/view
che abbiamo avuto un problema in cui lo stato iniziale del negozio veniva visualizzato brevemente mentre veniva effettuata la nostra chiamata API falsa? Un modo per risolvere questo problema è avere un booleano archiviato sul componente o nello Store come loading = true
e quindi visualizzare un componente di caricamento mentre la chiamata API termina. Successivamente, imposteremo loading = false
e visualizzeremo i dati.
Invece, usiamo fetch
per popolare lo Store prima del rendering. In una nuova pagina chiamata /products/view-async
, cambiamo il metodo created
in fetch
; dovrebbe funzionare, giusto?
export default { fetch () { // Unfortunately the below line throws an error // because 'this.$store' is undefined... this.$store.dispatch('product/load') }, computed: {...} }
Ecco il problema: questi metodi "sovralimentati" vengono eseguiti prima della creazione del componente, quindi this
non punta al componente e non è possibile accedere a nulla su di esso. Quindi, come accediamo allo Store qui?
L'API di contesto
Certo, c'è una soluzione. Su tutti i metodi di Nuxt, ti viene fornito un argomento (normalmente il primo) contenente un oggetto estremamente utile chiamato Context. In questo è tutto ciò a cui avrai bisogno di riferimento nell'app, il che significa che non dobbiamo aspettare che Vue crei prima quei riferimenti sul componente.
Consiglio vivamente di controllare i documenti Context per vedere cosa è disponibile. Alcuni utili sono l' app
, in cui puoi accedere a tutti i tuoi plug-in, redirect
, che può essere utilizzato per modificare i percorsi, l' error
per visualizzare la pagina di errore e alcuni autoesplicativi come route
, query
e store
.
Quindi, per accedere allo Store, possiamo destrutturare il Context ed estrarre da esso lo Store. Dobbiamo anche assicurarci di restituire una promessa in modo che Nuxt possa attendere che si risolva prima di eseguire il rendering del componente, quindi dobbiamo apportare una piccola modifica anche alla nostra azione Store.
// Component export default { fetch ({ store }) { return store.dispatch('product/load') }, computed: {...} } // Store Action load ({ commit }) { return new Promise(resolve => { setTimeout(() => { commit('update', { _id: 1, title: 'Product', price: 99.99 }) resolve() }, 1000) }) }
Puoi usare async/await o altri metodi a seconda del tuo stile di codifica, ma il concetto è lo stesso: stiamo dicendo a Nuxt di assicurarsi che la chiamata API termini e che lo Store venga aggiornato con il risultato prima di provare a eseguire il rendering del componente. Se provi a navigare in /products/view-async
, non vedrai il flash di contenuto in cui il prodotto si trova nel suo stato iniziale.
Puoi immaginare quanto possa essere utile in qualsiasi app Vue anche senza SSR. Il contesto è disponibile anche per tutti i middleware e per altri metodi Nuxt come NuxtServerInit
, che è un'azione di archivio speciale che viene eseguita prima dell'inizializzazione dello Store (un esempio è nella sezione successiva)
Considerazioni sull'utilizzo di SSR
Sono sicuro che molti (me compreso) che iniziano a utilizzare una tecnologia come Nuxt mentre la trattano come qualsiasi altro progetto Vue alla fine hanno colpito un muro in cui qualcosa che sappiamo normalmente funzionerebbe sembra impossibile in Nuxt. Man mano che vengono documentati più di questi avvertimenti, sarà più facile superare, ma la cosa principale da considerare quando si avvia il debug è che il client e il server sono due entità separate.
Quando accedi a una pagina inizialmente, viene inviata una richiesta a Nuxt, il server crea il più possibile di quella pagina e del resto dell'app, quindi il server te la invia. Quindi la responsabilità è sul client di continuare con la navigazione e caricare i blocchi quando ne ha bisogno.
Vogliamo che il server faccia il più possibile prima, ma a volte non ha accesso alle informazioni di cui ha bisogno, il che si traduce invece nel lavoro svolto lato client. O peggio, quando il contenuto finale presentato dal client è diverso da quello previsto dal server, al client viene detto di ricostruirlo da zero. Questa è una grande indicazione che qualcosa non va nella logica dell'applicazione. Per fortuna, verrà generato un errore nella console del tuo browser (in modalità di sviluppo) se questo inizia a verificarsi.
Facciamo un esempio di come risolvere un problema comune, la gestione delle sessioni. Immagina di avere un'app Vue in cui puoi accedere a un account e la tua sessione viene archiviata utilizzando un token (JWT, ad esempio) che decidi di mantenere in localStorage
. Quando accedi inizialmente al sito, vuoi autenticare quel token rispetto a un'API, che restituisce alcune informazioni utente di base se valide e le inserisce nello Store.
Dopo aver letto i documenti di Nuxt, vedi che esiste un metodo pratico chiamato NuxtServerInit
che ti consente di popolare in modo asincrono lo Store una volta al caricamento iniziale. Sembra perfetto! Quindi crei il tuo modulo utente nello Store e aggiungi l'azione appropriata nel file index.js
nella directory Store:
export const actions = { nuxtServerInit ({ dispatch }) { // localStorage should work, right? const token = localStorage.getItem('token') if (token) return dispatch('user/load', token) } }
Quando aggiorni la pagina, viene visualizzato un errore, localStorage is not defined
. Pensare a dove sta accadendo, ha senso. Questo metodo viene eseguito sul server, non ha idea di cosa sia archiviato in localStorage
sul client; infatti, non sa nemmeno cosa sia “localStorage”! Quindi non è un'opzione.
Allora qual è la soluzione? Ce ne sono pochi, in realtà. Puoi invece fare in modo che il client inizializzi lo Store ma finisca per perdere i vantaggi di SSR perché il client finisce per fare tutto il lavoro. Puoi impostare sessioni sul server e quindi utilizzarlo per autenticare l'utente, ma questo è un altro livello da configurare. La cosa più simile al metodo localStorage
è invece l'utilizzo dei cookie.
Nuxt ha accesso ai cookie perché vengono inviati con la richiesta dal client al server. Come con altri metodi Nuxt, nuxtServerInit
ha accesso al Context, questa volta come secondo argomento perché il primo è riservato allo store. Sul Context, possiamo accedere all'oggetto req
, che memorizza tutte le intestazioni e altre informazioni dalla richiesta del client. (Questo sarà particolarmente familiare se hai usato Node.js.)
Quindi, dopo aver memorizzato invece il token in un cookie (chiamato "token", in questo caso), accediamoci sul server.
import Cookie from 'cookie' export const actions = { nuxtServerInit ({ dispatch }, { req }) { const cookies = Cookie.parse(req.headers.cookie || '') const token = cookies['token'] || '' if (token) return dispatch('user/load', token) } }
Una soluzione semplice, ma che potrebbe non essere immediatamente ovvia. Imparare a pensare a dove stanno avvenendo determinate azioni (client, server o entrambi) ea cosa hanno accesso richiede del tempo, ma ne vale la pena.
Distribuzione
La distribuzione con Nuxt è estremamente semplice. Usando la stessa base di codice, puoi creare un'app SSR, un'applicazione a pagina singola o una pagina statica.
App con rendering lato server (app SSR)
Questo è probabilmente ciò a cui miravi quando utilizzavi Nuxt. Il concetto di base per la distribuzione qui è eseguire il processo di build
su qualsiasi piattaforma tu scelga e impostare alcune configurazioni. Userò l'esempio di Heroku dai documenti:
Innanzitutto, imposta gli script per Heroku in package.json
:
"scripts": { "dev": "nuxt", "build": "nuxt build", "start": "nuxt start", "heroku-postbuild": "npm run build" }
Quindi imposta l'ambiente Heroku usando heroku-cli
(istruzioni per la configurazione qui:
# set Heroku variables heroku config:set NPM_CONFIG_PRODUCTION=false heroku config:set HOST=0.0.0.0 heroku config:set NODE_ENV=production # deploy git push heroku master
Questo è tutto. Ora la tua app SSR Vue è pronta per essere vista dal mondo. Altre piattaforme hanno configurazioni diverse, ma il processo è simile. I metodi di distribuzione ufficiali attualmente elencati sono:
- Adesso
- Dokku (Oceano Digitale)
- Nginx
Applicazione a pagina singola (SPA)
Se desideri sfruttare alcune delle funzionalità extra fornite da Nuxt ma evitare che il server tenti di eseguire il rendering delle pagine, puoi invece eseguire la distribuzione come SPA.
Innanzitutto, è meglio testare l'applicazione senza SSR poiché per impostazione predefinita npm run dev
viene eseguito con SSR attivo. Per cambiarlo, modifica il file nuxt.config.js
e aggiungi la seguente opzione:
mode: 'spa',
Ora, quando esegui npm run dev
, SSR verrà disattivato e l'applicazione verrà eseguita come SPA da testare. Questa impostazione assicura inoltre che nessuna build futura includa SSR.
Se tutto sembra a posto, la distribuzione è esattamente la stessa di un'app SSR. Ricorda solo che devi impostare la mode: 'spa'
prima per far sapere al processo di build che vuoi una SPA.
Pagine statiche
Se non vuoi avere a che fare con un server e vuoi invece generare pagine da utilizzare con servizi di hosting statici come Surge o Netlify, allora questa è l'opzione da scegliere. Tieni solo presente che, senza un server, non sarai in grado di accedere a req
e res
nel Context, quindi se il tuo codice si basa su quello, assicurati di adattarlo. Ad esempio, durante la generazione del progetto di esempio, la funzione nuxtServerInit
genera un errore perché sta tentando di recuperare un token dai cookie nelle intestazioni della richiesta. In questo progetto, non importa, poiché i dati non vengono utilizzati da nessuna parte, ma in un'applicazione reale, dovrebbe esserci un modo alternativo per accedere a tali dati.
Una volta che è stato ordinato, la distribuzione è facile. Una cosa che probabilmente dovrai prima cambiare è aggiungere un'opzione in modo che il comando nuxt generate
crei anche un file di fallback. Questo file richiederà al servizio di hosting di consentire a Nuxt di gestire il routing anziché il servizio di hosting, generando un errore 404. Per fare ciò, aggiungi la seguente riga a nuxt.config.js
:
generate: { fallback: true },
Ecco un esempio che utilizza Netlify, che al momento non è nei documenti di Nuxt. Tieni presente che se è la prima volta che usi netlify-cli
, ti verrà chiesto di autenticarti:
# install netlify-cli globally npm install netlify-cli -g # generate the application (outputs to dist/ folder) npm run generate # deploy netlify deploy dist
E 'così semplice! Come accennato all'inizio dell'articolo, c'è una versione di questo progetto qui. C'è anche la documentazione ufficiale di distribuzione per i seguenti servizi di seguito:
- Ondeggiare
- Pagine GitHub
Per saperne di più
Nuxt si sta aggiornando rapidamente e questa è solo una piccola selezione delle funzionalità che offre. Spero che questo articolo ti incoraggi a provarlo e vedere se può aiutare a migliorare le capacità delle tue applicazioni Vue, permettendoti di sviluppare più velocemente e sfruttare le sue potenti funzionalità.
Se stai cercando maggiori informazioni, non cercare oltre i link ufficiali di Nuxt:
- Documentazione
- Playground
- GitHub
- FAQ
Looking to up your JavaScript game? Try reading The Comprehensive Guide to JavaScript Design Patterns by fellow Toptaler Marko Mišura.