Erstellen serverseitig gerenderter Vue.js-Apps mit Nuxt.js
Veröffentlicht: 2022-03-11JavaScript-Frameworks/Bibliotheken wie Vue können beim Surfen auf Ihrer Website ein fantastisches Benutzererlebnis bieten. Die meisten bieten eine Möglichkeit, den Seiteninhalt dynamisch zu ändern, ohne jedes Mal eine Anfrage an den Server senden zu müssen.
Bei diesem Ansatz gibt es jedoch ein Problem. Beim erstmaligen Laden Ihrer Website erhält Ihr Browser keine vollständige Seite zur Anzeige. Stattdessen erhält es eine Reihe von Teilen zum Erstellen der Seite (HTML, CSS, andere Dateien) und Anweisungen, wie sie alle zusammengefügt werden können (ein JavaScript-Framework/eine JavaScript-Bibliothek). Es dauert messbar, all diese Informationen zusammenzustellen bevor Ihr Browser tatsächlich etwas anzuzeigen hat. Es ist, als würde man einen Haufen Bücher zusammen mit einem flachen Bücherregal schicken. Sie müssten zuerst das Bücherregal bauen und es dann mit den Büchern füllen.
Die Lösung dafür ist clever: Haben Sie eine Version des Frameworks/der Bibliothek auf dem Server , die eine anzeigefertige Seite erstellen kann. Senden Sie dann diese vollständige Seite an den Browser, zusammen mit der Möglichkeit, weitere Änderungen vorzunehmen und dennoch über dynamische Seiteninhalte (das Framework / die Bibliothek) zu verfügen, genau wie wenn Sie ein fertiges Bücherregal zusammen mit einigen Büchern erhalten. Klar, man muss die Bücher noch ins Bücherregal stellen, aber man hat sofort etwas Brauchbares.
Abgesehen von der albernen Analogie gibt es noch eine Reihe weiterer Vorteile. Beispielsweise muss eine Seite, die sich selten ändert, wie z. B. eine „Über uns“-Seite, nicht jedes Mal neu erstellt werden, wenn ein Benutzer danach fragt. Ein Server kann es also einmal erstellen und es dann zwischenspeichern oder für die zukünftige Verwendung irgendwo speichern. Diese Art von Geschwindigkeitsverbesserungen mag winzig erscheinen, aber in einer Umgebung, in der die Zeit bis zur Reaktionsfähigkeit in Millisekunden (oder weniger) gemessen wird, zählt jedes kleine bisschen.
Wenn Sie weitere Informationen zu den Vorteilen von SSR in einer Vue-Umgebung wünschen, sollten Sie den Vue-eigenen Artikel zu SSR lesen. Es gibt eine Vielzahl von Optionen, um diese Ergebnisse zu erzielen, aber die beliebteste, die auch vom Vue-Team empfohlen wird, ist Nuxt.
Warum Nuxt.js
Nuxt.js basiert auf einer Implementierung von SSR für die beliebte React-Bibliothek namens Next. Nachdem die Vorteile dieses Designs erkannt wurden, wurde eine ähnliche Implementierung namens Nuxt für Vue entwickelt. Diejenigen, die mit der React+Next-Kombination vertraut sind, werden eine Reihe von Ähnlichkeiten in Design und Layout der Anwendung erkennen. Nuxt bietet jedoch Vue-spezifische Funktionen, um eine leistungsstarke und dennoch flexible SSR-Lösung für Vue zu erstellen.
Nuxt wurde im Januar 2018 auf eine produktionsreife Version 1.0 aktualisiert und ist Teil einer aktiven und gut unterstützten Community. Eines der großartigen Dinge ist, dass sich das Erstellen eines Projekts mit Nuxt nicht wesentlich vom Erstellen eines anderen Vue-Projekts unterscheidet. Tatsächlich bietet es eine Reihe von Funktionen, mit denen Sie gut strukturierte Codebasen in kürzerer Zeit erstellen können.
Ein weiterer wichtiger Punkt ist , dass Nuxt nicht für SSR verwendet werden muss . Es wird als Framework zum Erstellen universeller Vue.js-Anwendungen beworben und enthält einen Befehl ( nuxt generate
) zum Erstellen statisch generierter Vue-Anwendungen mit derselben Codebasis. Wenn Sie also Angst davor haben, tief in SSR einzutauchen, geraten Sie nicht in Panik. Sie können stattdessen jederzeit eine statische Site erstellen und trotzdem die Funktionen von Nuxt nutzen.
Um das Potenzial von Nuxt auszuschöpfen, erstellen wir ein einfaches Projekt. Der endgültige Quellcode für dieses Projekt wird auf GitHub gehostet, wenn Sie ihn sehen möchten, oder Sie können eine Live-Version anzeigen, die mit nuxt generate
und auf Netlify gehostet wird.
Erstellen eines Nuxt-Projekts
Lassen Sie uns zunächst einen Vue-Projektgenerator namens vue-cli
verwenden, um schnell ein Beispielprojekt zu erstellen:
# 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
Nachdem Sie einige Optionen durchlaufen haben, wird ein Projekt im Ordner my-nuxt-project
oder was auch immer Sie angegeben haben erstellt. Dann müssen wir nur Abhängigkeiten installieren und den Server ausführen:
cd my-nuxt-project npm install # Or yarn npm run dev
Na, bitte. Öffnen Sie Ihren Browser zu localhost:3000
und Ihr Projekt sollte ausgeführt werden. Nicht viel anders als beim Erstellen eines Vue Webpack-Projekts. Wenn wir uns jedoch die tatsächliche Struktur der App ansehen, gibt es nicht viel zu sehen, insbesondere im Vergleich zu etwas wie dem Vue Webpack-Template.
Ein Blick in die package.json
zeigt auch, dass wir nur eine Abhängigkeit haben, Nuxt selbst. Dies liegt daran, dass jede Version von Nuxt auf die Arbeit mit bestimmten Versionen von Vue, Vue-router und Vuex zugeschnitten ist und sie alle für Sie bündelt.
Es gibt auch eine nuxt.config.js
-Datei im Projektstammverzeichnis. Auf diese Weise können Sie eine Reihe von Funktionen anpassen, die Nuxt bietet. Standardmäßig werden die Header-Tags, die Farbe des Ladebalkens und die ESLint-Regeln für Sie festgelegt. Wenn Sie wissen möchten, was Sie konfigurieren können, finden Sie hier die Dokumentation. Wir werden einige Optionen in diesem Artikel behandeln.
Was ist also so besonders an diesen Verzeichnissen?
Projektlayout
Wenn Sie durch die erstellten Verzeichnisse blättern, haben alle eine begleitende Readme mit einer kurzen Zusammenfassung dessen, was in diesem Verzeichnis enthalten ist, und oft einen Link zu den Dokumenten.
Dies ist ein Vorteil der Verwendung von Nuxt: eine Standardstruktur für Ihre Anwendung. Jeder gute Frontend-Entwickler wird eine Anwendung ähnlich wie diese strukturieren, aber es gibt viele verschiedene Vorstellungen von Strukturen, und wenn Sie in einem Team arbeiten, wird zwangsläufig einige Zeit in die Diskussion oder Auswahl dieser Struktur fließen. Nuxt stellt Ihnen einen zur Verfügung.
Nuxt sucht nach bestimmten Verzeichnissen und erstellt Ihre Anwendung für Sie, je nachdem, was es findet. Sehen wir uns diese Verzeichnisse nacheinander an.
Seiten
Dies ist das einzige erforderliche Verzeichnis. Alle Vue-Komponenten in diesem Verzeichnis werden basierend auf ihren Dateinamen und der Verzeichnisstruktur automatisch zu vue-router
hinzugefügt. Das ist äußerst bequem. Normalerweise hätte ich sowieso ein separates Pages-Verzeichnis und müsste jede dieser Komponenten manuell in einer anderen Router-Datei registrieren. Diese Router-Datei kann bei größeren Projekten komplex werden und muss möglicherweise aufgeteilt werden, um die Lesbarkeit zu gewährleisten. Stattdessen übernimmt Nuxt diese gesamte Logik für Sie.
Zur Demonstration können wir eine Vue-Komponente namens about.vue
im Pages-Verzeichnis erstellen. Lassen Sie uns einfach eine einfache Vorlage hinzufügen, wie zum Beispiel:
<template> <h1>About Page</h1> </template>
Wenn Sie speichern, generiert Nuxt die Routen für Sie neu. Da wir unsere Komponente about.vue
genannt haben, sollten Sie diese Komponente sehen, wenn Sie zu /about
navigieren. Einfach.
Es gibt einen Dateinamen, der besonders ist. Wenn Sie eine Datei index.vue
, wird eine Root-Route für dieses Verzeichnis erstellt. Wenn das Projekt generiert wird, gibt es bereits eine index.vue
-Komponente im Seitenverzeichnis, die mit der Startseite oder Zielseite Ihrer Website korreliert. (Im Entwicklungsbeispiel wäre dies einfach localhost:3000
.)
Was ist mit tieferen Routen? Unterverzeichnisse im Pages-Verzeichnis helfen bei der Strukturierung Ihrer Routen. Wenn wir also eine Seite zum Anzeigen von Produkten haben wollten, könnten wir unser Seitenverzeichnis etwa so strukturieren:
/pages --| /products ----| index.vue ----| view.vue
Wenn wir nun zu /products/view
navigieren, sehen wir die Komponente view.vue
im Produktverzeichnis. Wenn wir stattdessen zu /products
navigieren, sehen wir die Komponente index.vue
im Produktverzeichnis.
Sie fragen sich vielleicht, warum wir nicht einfach eine products.vue
-Komponente im Seitenverzeichnis erstellt haben, sondern wie wir es für die /about
-Seite getan haben. Sie denken vielleicht, dass das Ergebnis dasselbe wäre, aber es gibt einen Unterschied zwischen den beiden Strukturen. Lassen Sie uns dies demonstrieren, indem wir eine weitere neue Seite hinzufügen.
Nehmen wir an, wir wollten für jeden Mitarbeiter eine separate About-Seite. Lassen Sie uns zum Beispiel eine About-Seite für mich erstellen. Es sollte sich unter /about/ben-jones
befinden. Zunächst können wir versuchen, das Pages-Verzeichnis wie folgt zu strukturieren:
/pages --| about.vue --| /about ----| ben-jones.vue
Wenn wir versuchen, auf /about/ben-jones
zuzugreifen, erhalten wir stattdessen die Komponente about.vue
, genau wie /about
. Was ist denn hier los?
Interessanterweise generiert Nuxt hier eine verschachtelte Route . Diese Struktur legt nahe, dass Sie eine permanente /about
-Route wünschen und alles innerhalb dieser Route in einem eigenen Ansichtsbereich verschachtelt sein sollte. In vue-router würde dies durch die Angabe einer <router-view />
Komponente innerhalb der about.vue
-Komponente gekennzeichnet. In Nuxt ist dies dasselbe Konzept, außer dass wir anstelle von <router-view />
einfach <nuxt />
verwenden. Aktualisieren wir also unsere about.vue
-Komponente, um verschachtelte Routen zu ermöglichen:
<template> <div> <h1>About Page</h1> <nuxt /> </div> </template>
Wenn wir jetzt zu /about
navigieren, erhalten wir die Komponente about.vue
, die wir zuvor hatten, mit nur einem Titel. Wenn wir jedoch zu /about/ben-jones
navigieren, werden stattdessen der Titel und die ben-jones.vue
Komponente dort gerendert, wo der Platzhalter <nuxt/>
war.
Das war ursprünglich nicht das, was wir wollten, aber die Idee, eine About-Seite mit einer Liste von Personen zu haben, die, wenn sie angeklickt werden, einen Abschnitt auf der Seite mit ihren Informationen füllen, ist ein interessantes Konzept, also lassen wir es erstmal so . Wenn Sie die andere Option wollten, dann würden wir nur unsere Verzeichnisse neu strukturieren. Wir müssten nur die Komponente about.vue
in das Verzeichnis /about
verschieben und sie index.vue
umbenennen, sodass die resultierende Struktur wie folgt wäre:
/pages --| /about ----| index.vue ----| ben-jones.vue
Nehmen wir abschließend an, wir wollten Routenparameter verwenden, um ein bestimmtes Produkt abzurufen. Beispielsweise möchten wir in der Lage sein, ein Produkt zu bearbeiten, indem wir zu /products/edit/64
navigieren, wobei 64 die product_id
ist. Wir können dies auf folgende Weise tun:
/pages --| /products ----| /edit ------| _product_id.vue
Beachten Sie den Unterstrich am Anfang der Komponente _product_id.vue
– dies kennzeichnet einen Routenparameter, auf den dann über das Objekt $route.params
oder über das Objekt params
in Nuxts Context zugegriffen werden kann (dazu später mehr). Beachten Sie, dass der Schlüssel für den Parameter der Komponentenname ohne den anfänglichen Unterstrich ist – in diesem Fall product_id
– versuchen Sie also, sie innerhalb des Projekts eindeutig zu halten. Infolgedessen haben wir in _product_id.vue
möglicherweise Folgendes:
<template> <h1>Editing Product {{ $route.params.product_id }}</h1> </template>
Sie können sich komplexere Layouts vorstellen, deren Einrichtung mit vue-router mühsam wäre. Zum Beispiel können wir alle oben genannten Punkte zu einer Route kombinieren, wie zum Beispiel:
/pages --| /categories ----| /_category_id ------| products.vue ------| /products --------| _product_id.vue
Es ist nicht allzu schwierig zu überlegen, was /categories/2/products/3
anzeigen würde. Wir hätten die Komponente products.vue
mit einer verschachtelten Komponente _product_id.vue
mit zwei Routenparametern: category_id
und product_id
. Dies ist viel einfacher zu begründen als eine entsprechende Router-Konfiguration.
Wo wir gerade beim Thema sind, eine Sache, die ich in der Router-Konfiguration neige, ist das Einrichten von Router-Wächtern. Da Nuxt den Router für uns baut, kann dies stattdessen auf der Komponente selbst mit beforeRouteEnter
. Wenn Sie Routenparameter validieren möchten, bietet Nuxt eine Komponentenmethode namens validate
. Wenn Sie also überprüfen möchten, ob die product_id
eine Zahl ist, bevor Sie versuchen, die Komponente zu rendern, fügen Sie dem script-Tag von _product_id.vue
Folgendes hinzu:
export default { validate ({ params }) { // Must be a number return /^\d+$/.test(params.product_id) } }
Wenn Sie jetzt zu /categories/2/products/someproduct
, wird ein 404-Fehler ausgegeben, weil someproduct
keine gültige Nummer ist.
Das war's für das Pages-Verzeichnis. Es ist wichtig zu lernen, wie Sie Ihre Routen in diesem Verzeichnis richtig strukturieren, daher ist es wichtig, anfangs ein wenig Zeit zu investieren, um das Beste aus Nuxt herauszuholen. Wenn Sie nach einer kurzen Übersicht suchen, ist es immer hilfreich, in der Dokumentation zum Routing nachzuschlagen.
Wenn Sie sich Sorgen machen, den Router nicht unter Kontrolle zu haben, seien Sie es nicht. Dieses Standard-Setup eignet sich hervorragend für eine Vielzahl von Projekten, vorausgesetzt, sie sind gut strukturiert. Es gibt jedoch einige Fälle, in denen Sie möglicherweise mehr Routen zum Router hinzufügen müssen, als Nuxt automatisch für Sie generiert, oder sie neu strukturieren müssen. Nuxt bietet eine Möglichkeit, die Router-Instanz in der Konfiguration anzupassen, sodass Sie neue Routen hinzufügen und generierte Routen anpassen können. Sie können auch die Kernfunktionalität der Routerinstanz bearbeiten, einschließlich zusätzlicher Optionen, die von Nuxt hinzugefügt wurden. Wenn Sie also auf einen Grenzfall stoßen, haben Sie immer noch die Flexibilität, die geeignete Lösung zu finden.
Speichern
Nuxt kann Ihren Vuex-Store basierend auf der Struktur des Store-Verzeichnisses erstellen, ähnlich dem Pages-Verzeichnis. Wenn Sie keinen Store benötigen, entfernen Sie einfach das Verzeichnis. Es gibt zwei Modi für den Store, Classic und Modules.
Classic erfordert , dass Sie eine index.js
-Datei im Store-Verzeichnis haben. Dort müssen Sie eine Funktion exportieren, die eine Vuex-Instanz zurückgibt:
import Vuex from 'vuex' const createStore = () => { return new Vuex.Store({ state: ..., mutations: ..., actions: ... }) } export default createStore
Auf diese Weise können Sie den Store nach Ihren Wünschen erstellen, ähnlich wie bei der Verwendung von Vuex in einem normalen Vue-Projekt.
Für den Modulmodus müssen Sie außerdem eine index.js
-Datei im Store-Verzeichnis erstellen. Diese Datei muss jedoch nur den Stammstatus/Mutationen/Aktionen für Ihren Vuex-Speicher exportieren. Das folgende Beispiel gibt einen leeren Stammstatus an:
export const state = () => ({})
Dann wird jede Datei im Store-Verzeichnis dem Store in ihrem eigenen Namespace oder Modul hinzugefügt. Lassen Sie uns zum Beispiel einen Ort erstellen, an dem das aktuelle Produkt gespeichert wird. Wenn wir eine Datei mit dem Namen product.js
im Store-Verzeichnis erstellen, ist ein Namespace-Abschnitt des Stores unter $store.product
verfügbar. Hier ist ein einfaches Beispiel dafür, wie diese Datei aussehen könnte:
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) } }
Das setTimeout
in der Ladeaktion simuliert eine Art API-Aufruf, der den Store mit der Antwort aktualisiert; in diesem Fall dauert es eine Sekunde. Jetzt verwenden wir es auf der Seite 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>
Ein paar Dinge zu beachten: Hier rufen wir unsere gefälschte API auf, wenn die Komponente erstellt wird. Sie können sehen, dass die product/load
, die wir senden, unter Produkt benannt ist. Damit ist klar, mit welcher Abteilung des Ladens wir es zu tun haben. Indem wir den Zustand dann einer lokalen berechneten Eigenschaft zuordnen, können wir ihn problemlos in unserer Vorlage verwenden.
Es gibt ein Problem: Wir sehen für eine Sekunde den Originalzustand, während die API läuft. Später werden wir eine von Nuxt bereitgestellte Lösung verwenden, um dies zu beheben (bekannt als fetch
).
Um das noch einmal zu betonen, wir mussten npm install vuex
, da es bereits im Nuxt-Paket enthalten ist. Wenn Sie dem Store-Verzeichnis eine index.js
-Datei hinzufügen, werden Ihnen alle diese Methoden automatisch geöffnet .
Das sind die beiden wichtigsten Verzeichnisse erklärt; der Rest ist viel einfacher.
Komponenten
Das Komponentenverzeichnis ist dazu da, Ihre wiederverwendbaren Komponenten wie Navigationsleiste, Bildergalerie, Paginierung, Datentabellen usw. zu enthalten. Da Komponenten im Seitenverzeichnis in Routen umgewandelt werden, müssen Sie diese Arten von Komponenten an anderer Stelle speichern. Auf diese Komponenten kann in Seiten oder anderen Komponenten zugegriffen werden, indem sie importiert werden:
import ComponentName from ~/components/ComponentName.vue
Vermögenswerte
Dies enthält unkompilierte Assets und hat mehr damit zu tun, wie Webpack Dateien lädt und verarbeitet, als damit, wie Nuxt funktioniert. Wenn Sie interessiert sind, empfehle ich Ihnen, die Anleitung in der Readme zu lesen.
Statisch
Diese enthält statische Dateien, die dem Stammverzeichnis Ihrer Website zugeordnet sind. Wenn Sie beispielsweise ein Bild namens logo.png in dieses Verzeichnis einfügen, wird es unter /logo.png
verfügbar. Dies ist gut für Metadateien wie robots.txt, favicon.ico und andere Dateien, die Sie benötigen.
Grundrisse
Normalerweise haben Sie in einem Vue-Projekt eine Art Root-Komponente, normalerweise App.vue
genannt. Hier können Sie Ihr (normalerweise statisches) App-Layout einrichten, das eine Navigationsleiste, eine Fußzeile und dann einen Inhaltsbereich für Ihren Vue-Router enthalten kann. Das default
Layout macht genau das und wird Ihnen im Layouts-Ordner zur Verfügung gestellt. Anfangs ist alles, was es hat, ein div mit einer <nuxt />
-Komponente (was äquivalent zu <router-view />
ist), aber es kann beliebig gestaltet werden. Zum Beispiel habe ich dem Beispielprojekt eine einfache Navigationsleiste hinzugefügt, um auf den verschiedenen Demonstrationsseiten zu navigieren.

Möglicherweise möchten Sie für einen bestimmten Abschnitt Ihrer App ein anderes Layout haben. Vielleicht haben Sie eine Art CMS oder Admin-Panel, das anders aussieht. Um dies zu lösen, erstellen Sie ein neues Layout im Layouts-Verzeichnis. Lassen Sie uns als Beispiel ein admin-layout.vue
Layout erstellen, das nur ein zusätzliches Header-Tag und keine Navigationsleiste hat:
<template> <div> <h1>Admin Layout</h1> <nuxt /> </div> </template>
Dann können wir eine admin.vue
-Seite im Pages-Verzeichnis erstellen und eine von Nuxt bereitgestellte Eigenschaft namens layout
verwenden, um den Namen (als Zeichenfolge) des Layouts anzugeben, das wir für diese Komponente verwenden möchten:
<template> <h1>Admin Page</h1> </template> <script> export default { layout: 'admin-layout' } </script>
Das ist alles dazu. Seitenkomponenten verwenden das default
, sofern nicht anders angegeben, aber wenn Sie zu /admin
navigieren, wird jetzt das Layout admin-layout.vue
verwendet. Natürlich kann dieses Layout auf Wunsch über mehrere Admin-Bildschirme hinweg geteilt werden. Die einzige wichtige Sache, die Sie sich merken sollten, ist, dass Layouts ein <nuxt />
-Element enthalten müssen .
Es gibt noch eine letzte Sache zu Layouts zu beachten. Möglicherweise ist Ihnen beim Experimentieren aufgefallen, dass Ihnen eine Fehlerseite angezeigt wird, wenn Sie eine ungültige URL eingeben. Diese Fehlerseite ist tatsächlich ein anderes Layout. Nuxt hat ein eigenes Fehlerlayout (Quellcode hier), aber wenn Sie es bearbeiten möchten, erstellen Sie einfach ein error.vue
-Layout, das stattdessen verwendet wird. Der Vorbehalt hier ist, dass das Fehlerlayout kein <nuxt />
-Element haben darf . Sie haben auch Zugriff auf ein error
auf der Komponente mit einigen grundlegenden Informationen zum Anzeigen. (Dies wird im Terminal ausgedruckt, auf dem Nuxt ausgeführt wird, wenn Sie es untersuchen möchten.)
Middleware
Middleware sind Funktionen, die vor dem Rendern einer Seite oder eines Layouts ausgeführt werden können. Es gibt eine Vielzahl von Gründen, warum Sie dies tun möchten. Route Guarding ist eine beliebte Verwendung, bei der Sie den Vuex-Store auf eine gültige Anmeldung überprüfen oder einige Parameter validieren können (anstatt die validate
-Methode auf der Komponente selbst zu verwenden). In einem Projekt, an dem ich kürzlich gearbeitet habe, wurde Middleware verwendet, um dynamische Breadcrumbs basierend auf der Route und den Parametern zu generieren.
Diese Funktionen können asynchron sein; Seien Sie jedoch vorsichtig, da dem Benutzer nichts angezeigt wird, bis die Middleware aufgelöst ist. Sie haben auch Zugriff auf Nuxts Context, was ich später erklären werde.
Plugins
In diesem Verzeichnis können Sie Vue-Plugins registrieren, bevor die Anwendung erstellt wird. Dadurch kann das Plugin in Ihrer gesamten App auf der Vue-Instanz geteilt werden und ist in jeder Komponente zugänglich.
Die meisten wichtigen Plugins haben eine Nuxt-Version, die einfach in der Vue-Instanz registriert werden kann, indem Sie ihren Dokumenten folgen. Es kann jedoch vorkommen, dass Sie ein Plug-in entwickeln oder ein vorhandenes Plug-in für diesen Zweck anpassen müssen. Ein Beispiel, das ich aus der Dokumentation ausgeliehen habe, zeigt, wie dies für vue-notifications
gemacht wird. Zuerst müssen wir das Paket installieren:
npm install vue-notifications --save
Erstellen Sie dann im Plugin-Verzeichnis eine Datei mit dem Namen vue-notifications.js
und fügen Sie Folgendes hinzu:
import Vue from 'vue' import VueNotifications from 'vue-notifications' Vue.use(VueNotifications)
Sehr ähnlich wie Sie ein Plugin in einer normalen Vue-Umgebung registrieren würden. Bearbeiten Sie dann die Datei nuxt.config.js
im Stammverzeichnis Ihres Projekts und fügen Sie den folgenden Eintrag zum Objekt module.exports hinzu:
plugins: ['~/plugins/vue-notifications']
Das ist es. Jetzt können Sie vue-notifications
in Ihrer gesamten App verwenden. Ein Beispiel dafür ist /plugin
im Beispielprojekt.
Damit ist ein Überblick über die Verzeichnisstruktur abgeschlossen. Es scheint viel zu lernen zu sein, aber wenn Sie eine Vue-App entwickeln, richten Sie bereits die gleiche Art von Logik ein. Nuxt hilft, das Setup zu abstrahieren und hilft Ihnen, sich auf das Bauen zu konzentrieren.
Nuxt hilft jedoch nicht nur bei der Entwicklung. Es lädt Ihre Komponenten auf, indem es zusätzliche Funktionalität bietet.
Nuxts aufgeladene Komponenten
Als ich anfing, Nuxt zu recherchieren, las ich immer wieder darüber, wie Page-Komponenten aufgeladen werden. Es klang großartig, aber es war nicht sofort ersichtlich, was genau das bedeutet und welche Vorteile es bringt.
Das bedeutet, dass allen Page-Komponenten zusätzliche Methoden angehängt sind, die Nuxt verwenden kann, um zusätzliche Funktionalität bereitzustellen. Tatsächlich haben wir so etwas bereits früher gesehen, als wir die Methode validate
verwendet haben, um Parameter zu überprüfen und einen Benutzer umzuleiten, wenn sie ungültig sind.
Die beiden wichtigsten Methoden, die in einem Nuxt-Projekt verwendet werden, sind die Methoden asyncData
und fetch
. Beide sind im Konzept sehr ähnlich, sie werden asynchron ausgeführt, bevor die Komponente generiert wird, und sie können verwendet werden, um die Daten einer Komponente und des Speichers zu füllen. Sie ermöglichen auch, dass die Seite vollständig auf dem Server gerendert wird, bevor sie an den Client gesendet wird, selbst wenn wir auf einen Datenbank- oder API-Aufruf warten müssen.
Was ist der Unterschied zwischen asyncData
und fetch
?
-
asyncData
wird verwendet, um die Daten der Page-Komponente zu füllen. Wenn Sie ein Objekt zurückgeben, wird es vor dem Rendern mit derdata
zusammengeführt. -
fetch
wird verwendet, um den Vuex Store zu füllen. Wenn Sie ein Versprechen zurückgeben, wartet Nuxt, bis es aufgelöst ist, bevor es gerendert wird.
Also lasst uns diese gut nutzen. Erinnern Sie sich, dass wir vorhin auf der Seite /products/view
ein Problem hatten, bei dem der Anfangszustand des Shops kurz angezeigt wurde, während unser gefälschter API-Aufruf durchgeführt wurde? Eine Möglichkeit, dies zu beheben, besteht darin, einen booleschen Wert in der Komponente oder im Store zu speichern, z. B. loading = true
und dann eine Ladekomponente anzuzeigen, während der API-Aufruf beendet wird. Danach würden wir loading = false
setzen und die Daten anzeigen.
Verwenden wir stattdessen fetch
, um den Store vor dem Rendern zu füllen. Auf einer neuen Seite mit dem Namen /products/view-async
ändern wir die created
Methode in fetch
; das sollte funktionieren oder?
export default { fetch () { // Unfortunately the below line throws an error // because 'this.$store' is undefined... this.$store.dispatch('product/load') }, computed: {...} }
Hier ist der Haken: Diese „aufgeladenen“ Methoden werden ausgeführt, bevor die Komponente erstellt wird, sodass this
nicht auf die Komponente zeigt und auf nichts darauf zugegriffen werden kann. Wie greifen wir also hier auf den Store zu?
Die Kontext-API
Natürlich gibt es eine Lösung. Bei allen Nuxt-Methoden erhalten Sie ein Argument (normalerweise das erste), das ein äußerst nützliches Objekt namens Kontext enthält. Dies ist alles, worauf Sie in der gesamten App verweisen müssen, was bedeutet, dass wir nicht warten müssen, bis Vue diese Verweise zuerst auf der Komponente erstellt.
Ich würde dringend empfehlen, sich die Context-Dokumentation anzusehen, um zu sehen, was verfügbar ist. Einige praktische sind app
, wo Sie auf alle Ihre Plugins zugreifen können, redirect
, das zum Ändern von Routen verwendet werden kann, error
zum Anzeigen der Fehlerseite und einige selbsterklärende wie route
, query
und store
.
Um auf den Store zuzugreifen, können wir also den Kontext destrukturieren und den Store daraus extrahieren. Wir müssen auch sicherstellen, dass wir ein Versprechen zurückgeben, damit Nuxt darauf warten kann, dass es aufgelöst wird, bevor die Komponente gerendert wird, also müssen wir auch eine kleine Anpassung an unserer Store-Aktion vornehmen.
// 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) }) }
Sie könnten async/await oder andere Methoden je nach Ihrem Codierungsstil verwenden, aber das Konzept ist das gleiche – wir weisen Nuxt an, sicherzustellen, dass der API-Aufruf beendet und der Store mit dem Ergebnis aktualisiert wird, bevor versucht wird, die Komponente zu rendern. Wenn Sie versuchen, zu /products/view-async
zu navigieren, sehen Sie nicht den Flash-Inhalt, in dem sich das Produkt in seinem ursprünglichen Zustand befindet.
Sie können sich vorstellen, wie nützlich dies in jeder Vue-App auch ohne SSR sein kann. Der Kontext ist auch für alle Middlewares sowie für andere Nuxt-Methoden wie NuxtServerInit
, bei der es sich um eine spezielle Store-Aktion handelt, die ausgeführt wird, bevor der Store initialisiert wird (ein Beispiel dafür finden Sie im nächsten Abschnitt).
Überlegungen zur Verwendung von SSR
Ich bin sicher, dass viele (mich eingeschlossen), die anfangen, eine Technologie wie Nuxt zu verwenden, während sie sie wie jedes andere Vue-Projekt behandeln, schließlich an eine Wand stoßen, wo etwas, von dem wir wissen, dass es normalerweise in Nuxt funktionieren würde, unmöglich erscheint. Je mehr dieser Vorbehalte dokumentiert werden, desto einfacher wird es, sie zu überwinden, aber die Hauptsache, die Sie zu Beginn des Debuggens berücksichtigen sollten, ist, dass der Client und der Server zwei separate Einheiten sind.
Wenn Sie zum ersten Mal auf eine Seite zugreifen, wird eine Anfrage an Nuxt gesendet, der Server erstellt so viel wie möglich von dieser Seite und dem Rest der App, und dann sendet der Server sie an Sie. Dann liegt die Verantwortung beim Client, mit der Navigation fortzufahren und Chunks nach Bedarf zu laden.
Wir möchten, dass der Server zuerst so viel wie möglich erledigt, aber manchmal hat er keinen Zugriff auf die benötigten Informationen, was dazu führt, dass die Arbeit stattdessen clientseitig erledigt wird. Oder schlimmer noch, wenn der endgültige Inhalt, der vom Client präsentiert wird, sich von dem unterscheidet, was der Server erwartet hat, wird der Client angewiesen, ihn von Grund auf neu zu erstellen. Dies ist ein deutlicher Hinweis darauf, dass etwas mit der Anwendungslogik nicht stimmt. Glücklicherweise wird in der Konsole Ihres Browsers (im Entwicklungsmodus) ein Fehler generiert, wenn dies auftritt.
Nehmen wir ein Beispiel für die Lösung eines häufigen Problems, der Sitzungsverwaltung. Stellen Sie sich vor, Sie haben eine Vue-App, in der Sie sich bei einem Konto anmelden können, und Ihre Sitzung wird mit einem Token (z. B. JWT) gespeichert, das Sie in localStorage
. Wenn Sie zum ersten Mal auf die Website zugreifen, möchten Sie dieses Token anhand einer API authentifizieren, die einige grundlegende Benutzerinformationen zurückgibt, falls gültig, und diese Informationen in den Store stellt.
Nachdem Sie die Dokumentation von Nuxt gelesen haben, sehen Sie, dass es eine praktische Methode namens NuxtServerInit
gibt, mit der Sie den Store einmal beim ersten Laden asynchron füllen können. Das klingt super! Sie erstellen also Ihr Benutzermodul im Store und fügen die entsprechende Aktion in der Datei index.js
im Store-Verzeichnis hinzu:
export const actions = { nuxtServerInit ({ dispatch }) { // localStorage should work, right? const token = localStorage.getItem('token') if (token) return dispatch('user/load', token) } }
Wenn Sie die Seite aktualisieren, erhalten Sie eine Fehlermeldung, localStorage is not defined
. Wenn man darüber nachdenkt, wo das passiert, macht es Sinn. Diese Methode wird auf dem Server ausgeführt, sie hat keine Ahnung, was in localStorage
auf dem Client gespeichert ist; Tatsächlich weiß es nicht einmal, was „localStorage“ ist! Das ist also keine Option.
Also, was ist die Lösung? Es gibt tatsächlich ein paar. Sie können den Client dazu bringen, stattdessen den Store zu initialisieren, aber am Ende verlieren Sie die Vorteile von SSR, da der Client am Ende die ganze Arbeit erledigt. Sie können Sitzungen auf dem Server einrichten und diese dann verwenden, um den Benutzer zu authentifizieren, aber das ist eine weitere Ebene, die eingerichtet werden muss. Was der localStorage
Methode am ähnlichsten ist, verwendet stattdessen Cookies.
Nuxt hat Zugriff auf Cookies, weil sie mit der Anfrage vom Client an den Server gesendet werden. Wie bei anderen Nuxt-Methoden hat nuxtServerInit
Zugriff auf den Kontext, diesmal als zweites Argument, da das erste für den Speicher reserviert ist. Auf dem Kontext können wir auf das req
Objekt zugreifen, das alle Header und andere Informationen aus der Client-Anfrage speichert. (Dies wird Ihnen besonders bekannt vorkommen, wenn Sie Node.js verwendet haben.)
Nachdem wir das Token stattdessen in einem Cookie (in diesem Fall „Token“ genannt) gespeichert haben, greifen wir auf dem Server darauf zu.
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) } }
Eine einfache Lösung, die jedoch möglicherweise nicht sofort offensichtlich ist. Zu lernen, darüber nachzudenken, wo bestimmte Aktionen stattfinden (Client, Server oder beides) und worauf sie Zugriff haben, braucht einige Zeit, aber die Vorteile sind es wert.
Einsatz
Die Bereitstellung mit Nuxt ist extrem einfach. Mit derselben Codebasis können Sie eine SSR-App, eine Single-Page-Anwendung oder eine statische Seite erstellen.
Serverseitig gerenderte App (SSR-App)
Dies ist wahrscheinlich das Ziel, das Sie bei der Verwendung von Nuxt angestrebt haben. Das grundlegende Konzept für die Bereitstellung hier besteht darin, den build
-Prozess auf einer beliebigen Plattform auszuführen und einige Konfigurationen festzulegen. Ich verwende das Heroku-Beispiel aus der Dokumentation:
Richten Sie zunächst Skripte für Heroku in package.json
:
"scripts": { "dev": "nuxt", "build": "nuxt build", "start": "nuxt start", "heroku-postbuild": "npm run build" }
Richten Sie dann die Heroku-Umgebung mit heroku-cli
(Einrichtungsanleitung hier:
# 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
Das ist es. Jetzt ist Ihre SSR Vue-App live bereit, damit die Welt sie sehen kann. Andere Plattformen haben andere Einstellungen, aber der Prozess ist ähnlich. Die derzeit aufgeführten offiziellen Bereitstellungsmethoden sind:
- Jetzt
- Dokku (Digitaler Ozean)
- Nginx
Einseitige Bewerbung (SPA)
Wenn Sie einige der zusätzlichen Funktionen nutzen möchten, die Nuxt bietet, aber vermeiden möchten, dass der Server versucht, Seiten zu rendern, können Sie stattdessen als SPA bereitstellen.
Zunächst ist es am besten, Ihre Anwendung ohne SSR zu testen, da npm run dev
standardmäßig mit aktiviertem SSR ausgeführt wird. Um dies zu ändern, bearbeiten Sie die Datei nuxt.config.js
und fügen Sie die folgende Option hinzu:
mode: 'spa',
Wenn Sie jetzt npm run dev
, wird SSR deaktiviert und die Anwendung wird als SPA zum Testen ausgeführt. Diese Einstellung stellt auch sicher, dass keine zukünftigen Builds SSR enthalten.
Wenn alles in Ordnung aussieht, ist die Bereitstellung genau die gleiche wie bei einer SSR-App. Denken Sie nur daran, dass Sie mode: 'spa'
zuerst festlegen müssen, um dem Build-Prozess mitzuteilen, dass Sie ein SPA wünschen.
Statische Seiten
Wenn Sie sich überhaupt nicht mit einem Server befassen möchten und stattdessen Seiten für die Verwendung mit statischen Hosting-Diensten wie Surge oder Netlify generieren möchten, ist dies die Option, die Sie wählen sollten. Denken Sie nur daran, dass Sie ohne einen Server nicht auf req
und res
im Kontext zugreifen können. Wenn Ihr Code also darauf angewiesen ist, stellen Sie sicher, dass Sie dies berücksichtigen. Beim Generieren des Beispielprojekts gibt die Funktion nuxtServerInit
beispielsweise einen Fehler aus, weil sie versucht, ein Token aus den Cookies in den Anforderungsheadern abzurufen. In diesem Projekt spielt es keine Rolle, da diese Daten nirgendwo verwendet werden, aber in einer echten Anwendung müsste es eine alternative Möglichkeit geben, auf diese Daten zuzugreifen.
Sobald dies geklärt ist, ist die Bereitstellung einfach. Eine Sache, die Sie wahrscheinlich zuerst ändern müssen, ist das Hinzufügen einer Option, damit der Befehl nuxt generate
auch eine Fallback-Datei erstellt. Diese Datei fordert den Hosting-Service auf, das Routing von Nuxt und nicht vom Hosting-Service übernehmen zu lassen, was einen 404-Fehler auslöst. Fügen Sie dazu die folgende Zeile zu nuxt.config.js
:
generate: { fallback: true },
Hier ist ein Beispiel mit Netlify, das derzeit nicht in den Nuxt-Dokumenten enthalten ist. Denken Sie daran, dass Sie, wenn Sie netlify-cli
zum ersten Mal verwenden, aufgefordert werden, sich zu authentifizieren:
# install netlify-cli globally npm install netlify-cli -g # generate the application (outputs to dist/ folder) npm run generate # deploy netlify deploy dist
So einfach ist das! Wie am Anfang des Artikels erwähnt, gibt es hier eine Version dieses Projekts. Es gibt auch eine offizielle Bereitstellungsdokumentation für die folgenden Dienste unten:
- Anstieg
- GitHub-Seiten
Erfahren Sie mehr
Nuxt wird schnell aktualisiert, und dies ist nur eine kleine Auswahl der angebotenen Funktionen. Ich hoffe, dieser Artikel ermutigt Sie, es auszuprobieren und zu sehen, ob es dazu beitragen kann, die Fähigkeiten Ihrer Vue-Anwendungen zu verbessern, sodass Sie schneller entwickeln und die Vorteile seiner leistungsstarken Funktionen nutzen können.
Wenn Sie nach weiteren Informationen suchen, dann suchen Sie nicht weiter als die offiziellen Links von Nuxt:
- Dokumentation
- Spielplatz
- GitHub
- FAQ
Looking to up your JavaScript game? Try reading The Comprehensive Guide to JavaScript Design Patterns by fellow Toptaler Marko Mišura.