Création d'applications Vue.js rendues côté serveur à l'aide de Nuxt.js
Publié: 2022-03-11Les frameworks/bibliothèques JavaScript tels que Vue peuvent offrir une expérience utilisateur fantastique lors de la navigation sur votre site. La plupart offrent un moyen de modifier dynamiquement le contenu de la page sans avoir à envoyer une requête au serveur à chaque fois.
Cependant, il y a un problème avec cette approche. Lors du premier chargement de votre site Web, votre navigateur ne reçoit pas une page complète à afficher. Au lieu de cela, il reçoit un tas de pièces pour construire la page (HTML, CSS, autres fichiers) et des instructions sur la façon de les assembler (un framework/bibliothèque JavaScript). Il faut un temps mesurable pour rassembler toutes ces informations. avant que votre navigateur ait réellement quelque chose à afficher. C'est comme recevoir un tas de livres avec une bibliothèque à plat. Vous devez d'abord construire la bibliothèque, puis la remplir avec les livres.
La solution à cela est astucieuse : avoir une version du framework/de la bibliothèque sur le serveur qui peut créer une page prête à être affichée. Envoyez ensuite cette page complète au navigateur avec la possibilité d'apporter d'autres modifications et d'avoir toujours un contenu de page dynamique (le cadre/la bibliothèque), tout comme si vous receviez une bibliothèque toute faite avec quelques livres. Bien sûr, vous devez toujours mettre les livres dans la bibliothèque, mais vous avez quelque chose d'utilisable immédiatement.
Au-delà de l'analogie idiote, il y a aussi un tas d'autres avantages. Par exemple, une page qui change rarement, telle qu'une page À propos de nous, n'a pas besoin d'être recréée à chaque fois qu'un utilisateur le demande. Ainsi, un serveur peut le créer une fois, puis le mettre en cache ou le stocker quelque part pour une utilisation future. Ces types d'améliorations de la vitesse peuvent sembler minimes, mais dans un environnement où le temps jusqu'à la réactivité est mesuré en millisecondes (ou moins), chaque petit geste compte.
Si vous souhaitez plus d'informations sur les avantages du SSR dans un environnement Vue, vous devriez consulter le propre article de Vue sur le SSR. Il existe une variété d'options pour obtenir ces résultats, mais la plus populaire, qui est également recommandée par l'équipe Vue, est Nuxt.
Pourquoi Nuxt.js
Nuxt.js est basé sur une implémentation de SSR pour la populaire bibliothèque React appelée Next. Après avoir vu les avantages de cette conception, une implémentation similaire a été conçue pour Vue appelée Nuxt. Ceux qui connaissent la combinaison React + Next remarqueront un tas de similitudes dans la conception et la mise en page de l'application. Cependant, Nuxt propose des fonctionnalités spécifiques à Vue pour créer une solution SSR puissante mais flexible pour Vue.
Nuxt a été mis à jour vers une version 1.0 prête pour la production en janvier 2018 et fait partie d'une communauté active et bien soutenue. L'une des grandes choses est que la construction d'un projet à l'aide de Nuxt n'est pas si différente de la construction de tout autre projet Vue. En fait, il fournit un tas de fonctionnalités qui vous permettent de créer des bases de code bien structurées en un temps réduit.
Une autre chose importante à noter est que Nuxt n'a pas besoin d'être utilisé pour SSR . Il est présenté comme un cadre pour la création d'applications Vue.js universelles et inclut une commande ( nuxt generate
) pour créer des applications Vue générées statiques en utilisant la même base de code. Donc, si vous craignez de plonger profondément dans SSR, ne paniquez pas. Vous pouvez toujours créer un site statique à la place tout en profitant des fonctionnalités de Nuxt.
Afin de saisir le potentiel de Nuxt, créons un projet simple. Le code source final de ce projet est hébergé sur GitHub si vous souhaitez le voir, ou vous pouvez afficher une version en direct créée à l'aide nuxt generate
et hébergée sur Netlify.
Créer un projet Nuxt
Pour commencer, utilisons un générateur de projet Vue appelé vue-cli
pour créer rapidement un exemple de projet :
# 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
Après avoir parcouru quelques options, cela créera un projet dans le dossier my-nuxt-project
ou tout ce que vous avez spécifié. Ensuite, il nous suffit d'installer les dépendances et de lancer le serveur :
cd my-nuxt-project npm install # Or yarn npm run dev
Nous y voilà. Ouvrez votre navigateur sur localhost:3000
et votre projet devrait être en cours d'exécution. Pas très différent de la création d'un projet Vue Webpack. Cependant, lorsque nous examinons la structure réelle de l'application, il n'y a pas grand-chose, surtout par rapport à quelque chose comme le modèle Vue Webpack.
Regarder dans le package.json
montre également que nous n'avons qu'une seule dépendance, Nuxt lui-même. En effet, chaque version de Nuxt est conçue pour fonctionner avec des versions spécifiques de Vue, Vue-router et Vuex et les regroupe toutes pour vous.
Il existe également un fichier nuxt.config.js
à la racine du projet. Cela vous permet de personnaliser un tas de fonctionnalités fournies par Nuxt. Par défaut, il définit les balises d'en-tête, la couleur de la barre de chargement et les règles ESLint pour vous. Si vous êtes impatient de voir ce que vous pouvez configurer, voici la documentation ; nous couvrirons certaines options dans cet article.
Alors qu'y a-t-il de si spécial dans ces répertoires ?
Mise en page du projet
Si vous parcourez les répertoires créés, ils sont tous accompagnés d'un fichier Readme indiquant un bref résumé de ce qui se passe dans ce répertoire et souvent un lien vers la documentation.
C'est l'un des avantages de l'utilisation de Nuxt : une structure par défaut pour votre application. Tout bon développeur front-end structurera une application similaire à celle-ci, mais il existe de nombreuses idées différentes sur les structures, et lorsque vous travaillez en équipe, un certain temps sera inévitablement consacré à la discussion ou au choix de cette structure. Nuxt vous en fournit un.
Nuxt recherchera certains répertoires et construira votre application pour vous en fonction de ce qu'il trouve. Examinons ces répertoires un par un.
pages
C'est le seul répertoire requis . Tous les composants Vue de ce répertoire sont automatiquement ajoutés à vue-router
en fonction de leurs noms de fichiers et de la structure des répertoires. C'est extrêmement pratique. Normalement, j'aurais de toute façon un répertoire Pages séparé et je devrais enregistrer manuellement chacun de ces composants dans un autre fichier de routeur. Ce fichier de routeur peut devenir complexe pour les projets plus importants et peut nécessiter un fractionnement pour maintenir la lisibilité. Au lieu de cela, Nuxt gérera toute cette logique pour vous.
Pour illustrer, nous pouvons créer un composant Vue appelé about.vue
dans le répertoire Pages. Ajoutons simplement un modèle simple tel que :
<template> <h1>About Page</h1> </template>
Lorsque vous enregistrez, Nuxt régénère les itinéraires pour vous. Étant donné que nous avons appelé notre composant about.vue
, si vous naviguez vers /about
, vous devriez voir ce composant. Simple.
Il y a un nom de fichier qui est spécial. Nommer un fichier index.vue
créera une route racine pour ce répertoire. Lorsque le projet est généré, il existe déjà un composant index.vue
dans le répertoire des pages qui correspond à la page d'accueil ou à la page de destination de votre site. (Dans l'exemple de développement, ce serait simplement localhost:3000
.)
Qu'en est-il des voies plus profondes ? Les sous-répertoires du répertoire Pages aident à structurer vos routes. Donc, si nous voulions une page Afficher le produit, nous pourrions structurer notre répertoire Pages comme ceci :
/pages --| /products ----| index.vue ----| view.vue
Maintenant, si nous naviguons vers /products/view
, nous verrons le composant view.vue
dans le répertoire des produits. Si nous naviguons plutôt vers /products
, nous verrons le composant index.vue
dans le répertoire des produits.
Vous demandez peut-être pourquoi nous n'avons pas simplement créé un composant products.vue
dans le répertoire pages à la place comme nous l'avons fait pour la page /about
. Vous pensez peut-être que le résultat serait le même, mais il y a une différence entre les deux structures. Démontrons cela en ajoutant une autre nouvelle page.
Supposons que nous voulions une page À propos distincte pour chaque employé. Par exemple, créons une page À propos pour moi. Il devrait être situé à /about/ben-jones
. Au départ, nous pouvons essayer de structurer le répertoire Pages comme ceci :
/pages --| about.vue --| /about ----| ben-jones.vue
Lorsque nous essayons d'accéder à /about/ben-jones
, nous obtenons à la place le composant about.vue
, identique à /about
. Que se passe t-il ici?
Fait intéressant, ce que Nuxt fait ici génère une route imbriquée . Cette structure suggère que vous voulez un itinéraire permanent /about
et que tout ce qui se trouve à l'intérieur de cet itinéraire doit être imbriqué dans sa propre zone de vue. Dans vue-router, cela serait signifié en spécifiant un composant <router-view />
à l'intérieur du composant about.vue
. Dans Nuxt, c'est le même concept sauf qu'au lieu de <router-view />
, nous utilisons simplement <nuxt />
. Mettons donc à jour notre composant about.vue
pour autoriser les routes imbriquées :
<template> <div> <h1>About Page</h1> <nuxt /> </div> </template>
Maintenant, lorsque nous naviguons vers /about
, nous obtenons le composant about.vue
que nous avions auparavant, avec juste un titre. Cependant, lorsque nous naviguons vers /about/ben-jones
, nous avons à la place le titre et le composant ben-jones.vue
rendus là où se trouvait l'espace réservé <nuxt/>
.
Ce n'était pas ce que nous voulions au départ, mais l'idée d'avoir une page À propos avec une liste de personnes qui, lorsqu'on clique dessus, remplit une section de la page avec leurs informations est un concept intéressant, alors laissons-le tel quel pour l'instant . Si vous vouliez l'autre option, alors tout ce que nous ferions serait de restructurer nos répertoires. Nous n'aurions qu'à déplacer le composant about.vue
dans le répertoire /about
et le renommer index.vue
, de sorte que la structure résultante serait :
/pages --| /about ----| index.vue ----| ben-jones.vue
Enfin, disons que nous voulions utiliser les paramètres de route pour récupérer un produit spécifique. Par exemple, nous voulons pouvoir modifier un produit en accédant à /products/edit/64
où 64 est le product_id
. Nous pouvons le faire de la manière suivante :
/pages --| /products ----| /edit ------| _product_id.vue
Notez le trait de soulignement au début du composant _product_id.vue
- cela signifie un paramètre de route qui est ensuite accessible sur l'objet $route.params
ou sur l'objet params
dans le contexte de Nuxt (plus à ce sujet plus tard). Notez que la clé du paramètre sera le nom du composant sans le trait de soulignement initial - dans ce cas, product_id
- essayez donc de les garder uniques dans le projet. En conséquence, dans _product_id.vue
, nous pouvons avoir quelque chose comme :
<template> <h1>Editing Product {{ $route.params.product_id }}</h1> </template>
Vous pouvez commencer à imaginer des mises en page plus complexes, ce qui serait pénible à configurer avec vue-router. Par exemple, nous pouvons combiner tout ce qui précède dans un itinéraire tel que :
/pages --| /categories ----| /_category_id ------| products.vue ------| /products --------| _product_id.vue
Il n'est pas trop difficile de raisonner sur ce que /categories/2/products/3
afficherait. Nous aurions le composant products.vue
avec un composant _product_id.vue
imbriqué , avec deux paramètres de route : category_id
et product_id
. C'est beaucoup plus simple à raisonner qu'une configuration de routeur équivalente.
Pendant que nous sommes sur le sujet, une chose que j'ai tendance à faire dans la configuration du routeur est de configurer des gardes de routeur. Comme Nuxt construit le routeur pour nous, cela peut être fait à la place sur le composant lui-même avec beforeRouteEnter
. Si vous souhaitez valider les paramètres de route, Nuxt fournit une méthode de composant appelée validate
. Donc, si vous vouliez vérifier si le product_id
était un nombre avant d'essayer de rendre le composant, vous ajouteriez ce qui suit à la balise de script de _product_id.vue
:
export default { validate ({ params }) { // Must be a number return /^\d+$/.test(params.product_id) } }
Désormais, la navigation vers /categories/2/products/someproduct
entraîne un 404 car someproduct
n'est pas un numéro valide.
C'est tout pour le répertoire Pages. Apprendre à structurer correctement vos routes dans ce répertoire est essentiel, donc passer un peu de temps au départ est important pour tirer le meilleur parti de Nuxt. Si vous cherchez un bref aperçu, il est toujours utile de se référer aux docs pour le routage.
Si vous craignez de ne pas contrôler le routeur, ne le soyez pas. Cette configuration par défaut fonctionne très bien pour une grande variété de projets, à condition qu'ils soient bien structurés. Cependant, dans certains cas, vous devrez peut-être ajouter plus de routes au routeur que Nuxt ne génère automatiquement pour vous ou les restructurer. Nuxt fournit un moyen de personnaliser l'instance du routeur dans la configuration, vous permettant d'ajouter de nouvelles routes et de personnaliser les routes générées. Vous pouvez également modifier les fonctionnalités de base de l'instance de routeur, y compris les options supplémentaires ajoutées par Nuxt. Ainsi, si vous rencontrez un cas marginal, vous avez toujours la possibilité de trouver la solution appropriée.
Boutique
Nuxt peut créer votre magasin Vuex en fonction de la structure du répertoire du magasin, similaire au répertoire Pages. Si vous n'avez pas besoin d'un magasin, supprimez simplement le répertoire. Il existe deux modes pour le magasin, Classic et Modules.
Classic nécessite que vous ayez un fichier index.js
dans le répertoire du magasin. Là, vous devez exporter une fonction qui renvoie une instance Vuex :
import Vuex from 'vuex' const createStore = () => { return new Vuex.Store({ state: ..., mutations: ..., actions: ... }) } export default createStore
Cela vous permet de créer le magasin comme vous le souhaitez, un peu comme si vous utilisiez Vuex dans un projet Vue normal.
Le mode modules nécessite également que vous créiez un fichier index.js
dans le répertoire du magasin. Cependant, ce fichier n'a besoin que d'exporter l'état racine/les mutations/les actions de votre boutique Vuex. L'exemple ci-dessous spécifie un état racine vierge :
export const state = () => ({})
Ensuite, chaque fichier du répertoire du magasin sera ajouté au magasin dans son propre espace de noms ou module. Par exemple, créons un endroit pour stocker le produit actuel. Si nous créons un fichier appelé product.js
dans le répertoire du magasin, une section avec espace de noms du magasin sera disponible sur $store.product
. Voici un exemple simple de ce à quoi ce fichier peut ressembler :
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) } }
Le setTimeout
dans l'action de chargement simule une sorte d'appel d'API, qui mettra à jour le magasin avec la réponse ; dans ce cas, cela prend une seconde. Maintenant, utilisons-le dans la page 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>
Quelques points à noter : Ici, nous appelons notre fausse API lors de la création du composant. Vous pouvez voir que l'action product/load
que nous distribuons est placée sous Product. Cela indique exactement à quelle section du magasin nous avons affaire. Ensuite, en mappant l'état sur une propriété calculée locale, nous pouvons facilement l'utiliser dans notre modèle.
Il y a un problème : nous voyons l'état d'origine pendant une seconde pendant que l'API s'exécute. Plus tard, nous utiliserons une solution fournie par Nuxt pour résoudre ce problème (appelée fetch
).
Juste pour souligner encore une fois, nous n'avons jamais eu à npm install vuex
, car il est déjà inclus dans le package Nuxt. Lorsque vous ajoutez un fichier index.js
au répertoire du magasin, toutes ces méthodes vous sont alors automatiquement ouvertes.
Ce sont les deux principaux répertoires expliqués; le reste est beaucoup plus simple.
Composants
Le répertoire Components est là pour contenir vos composants réutilisables tels qu'une barre de navigation, une galerie d'images, une pagination, des tables de données, etc. Étant donné que les composants du répertoire Pages sont convertis en itinéraires, vous avez besoin d'un autre endroit pour stocker ces types de composants. Ces composants sont accessibles dans des pages ou d'autres composants en les important :
import ComponentName from ~/components/ComponentName.vue
Actifs
Cela contient des actifs non compilés et a plus à voir avec la façon dont Webpack charge et traite les fichiers, plutôt qu'avec le fonctionnement de Nuxt. Si vous êtes intéressé, je vous suggère de lire le guide dans le Readme.
Statique
Celui-ci contient des fichiers statiques qui sont mappés au répertoire racine de votre site. Par exemple, placer une image appelée logo.png dans ce répertoire la rendrait disponible sur /logo.png
. C'est bon pour les fichiers méta comme robots.txt, favicon.ico et d'autres fichiers dont vous avez besoin.
Dispositions
Normalement, dans un projet Vue, vous avez une sorte de composant racine, normalement appelé App.vue
. C'est ici que vous pouvez configurer la mise en page de votre application (normalement statique), qui peut inclure une barre de navigation, un pied de page, puis une zone de contenu pour votre vue-router. La mise en page default
fait exactement cela et vous est fournie dans le dossier des mises en page. Initialement, tout ce qu'il a est un div avec un <nuxt />
(qui équivaut à <router-view />
) mais il peut être stylisé comme vous le souhaitez. Par exemple, j'ai ajouté une simple barre de navigation au projet d'exemple pour la navigation dans les différentes pages de démonstration.

Vous voudrez peut-être avoir une mise en page différente pour une certaine section de votre application. Peut-être avez-vous une sorte de CMS ou de panneau d'administration qui semble différent. Pour résoudre ce problème, créez un nouveau layout dans le répertoire Layouts. Par exemple, créons une mise en page admin-layout.vue
qui a juste une balise d'en-tête supplémentaire et pas de barre de navigation :
<template> <div> <h1>Admin Layout</h1> <nuxt /> </div> </template>
Ensuite, nous pouvons créer une page admin.vue
dans le répertoire Pages et utiliser une propriété fournie par Nuxt appelée layout
pour spécifier le nom (sous forme de chaîne) de la mise en page que nous voulons utiliser pour ce composant :
<template> <h1>Admin Page</h1> </template> <script> export default { layout: 'admin-layout' } </script>
C'est tout ce qu'on peut en dire. Les composants de la page utiliseront la mise en page default
, sauf indication contraire, mais lorsque vous accédez à /admin
, il utilise désormais la mise en page admin-layout.vue
. Bien sûr, cette mise en page peut être partagée sur plusieurs écrans d'administration si vous le souhaitez. La seule chose importante à retenir est que les mises en page doivent contenir un <nuxt />
.
Il y a une dernière chose à noter à propos des mises en page. Vous avez peut-être remarqué en expérimentant que si vous tapez une URL invalide, une page d'erreur s'affiche. Cette page d'erreur est, en fait, une autre mise en page. Nuxt a sa propre mise en page d'erreur (code source ici), mais si vous vouliez la modifier, créez simplement une mise en page error.vue
et celle-ci sera utilisée à la place. La mise en garde ici est que la mise en page d'erreur ne doit pas avoir d'élément <nuxt />
. Vous aurez également accès à un objet d' error
sur le composant avec quelques informations de base à afficher. (Ceci est imprimé dans le terminal exécutant Nuxt si vous souhaitez l'examiner.)
Intergiciel
Les intergiciels sont des fonctions qui peuvent être exécutées avant le rendu d'une page ou d'une mise en page. Il existe une variété de raisons pour lesquelles vous voudrez peut-être le faire. La protection de route est une utilisation populaire où vous pouvez vérifier le magasin Vuex pour une connexion valide ou valider certains paramètres (au lieu d'utiliser la méthode de validate
sur le composant lui-même). Un projet sur lequel j'ai travaillé a récemment utilisé un middleware pour générer des fils d'Ariane dynamiques basés sur la route et les paramètres.
Ces fonctions peuvent être asynchrones ; soyez juste prudent, car rien ne sera montré à l'utilisateur jusqu'à ce que le middleware soit résolu. Ils ont également accès au contexte de Nuxt, que j'expliquerai plus tard.
Plugins
Ce répertoire vous permet d'enregistrer les plugins Vue avant la création de l'application. Cela permet au plug-in d'être partagé dans l'ensemble de votre application sur l'instance Vue et d'être accessible dans n'importe quel composant.
La plupart des principaux plugins ont une version Nuxt qui peut être facilement enregistrée sur l'instance Vue en suivant leur documentation. Cependant, il y aura des circonstances où vous développerez un plugin ou devrez adapter un plugin existant à cette fin. Un exemple que j'emprunte à la documentation montre comment procéder pour vue-notifications
. Tout d'abord, nous devons installer le package :
npm install vue-notifications --save
Créez ensuite un fichier dans le répertoire des plugins appelé vue-notifications.js
et incluez les éléments suivants :
import Vue from 'vue' import VueNotifications from 'vue-notifications' Vue.use(VueNotifications)
Très similaire à la façon dont vous enregistreriez un plugin dans un environnement Vue normal. Modifiez ensuite le fichier nuxt.config.js
à la racine de votre projet et ajoutez l'entrée suivante à l'objet module.exports :
plugins: ['~/plugins/vue-notifications']
C'est ça. Vous pouvez désormais utiliser vue-notifications
dans l'ensemble de votre application. Un exemple de ceci est à /plugin
dans le projet d'exemple.
Cela complète donc un aperçu de la structure des répertoires. Cela peut sembler beaucoup à apprendre, mais si vous développez une application Vue, vous configurez déjà le même type de logique. Nuxt aide à faire abstraction de la configuration et vous aide à vous concentrer sur la construction.
Nuxt fait cependant plus qu'aider au développement. Il suralimente vos composants en offrant des fonctionnalités supplémentaires.
Les composants suralimentés de Nuxt
Lorsque j'ai commencé à faire des recherches sur Nuxt, j'ai continué à lire sur la façon dont les composants Page sont suralimentés . Cela sonnait bien, mais ce que cela signifiait exactement et quels avantages cela apportait n'était pas immédiatement évident.
Cela signifie que tous les composants Page ont des méthodes supplémentaires qui leur sont attachées que Nuxt peut utiliser pour fournir des fonctionnalités supplémentaires. En fait, nous en avons déjà vu un plus tôt lorsque nous avons utilisé la méthode validate
pour vérifier les paramètres et rediriger un utilisateur s'ils ne sont pas valides.
Les deux principales utilisées dans un projet Nuxt seront les méthodes asyncData
et fetch
. Les deux sont très similaires dans leur concept, ils sont exécutés de manière asynchrone avant que le composant ne soit généré et ils peuvent être utilisés pour remplir les données d'un composant et du magasin. Ils permettent également à la page d'être entièrement rendue sur le serveur avant de l'envoyer au client même lorsque nous devons attendre un appel de base de données ou d'API.
Quelle est la différence entre asyncData
et fetch
?
-
asyncData
est utilisé pour remplir les données du composant Page. Lorsque vous renvoyez un objet, il est ensuite fusionné avec la sortie desdata
avant le rendu. -
fetch
est utilisé pour remplir le Vuex Store. Si vous retournez une promesse, Nuxt attendra qu'elle soit résolue avant de rendre.
Utilisons-les donc à bon escient. Vous vous souvenez plus tôt sur la page /products/view
que nous avons eu un problème où l'état initial du magasin était brièvement affiché pendant que notre faux appel API était effectué ? Une façon de résoudre ce problème consiste à stocker un booléen sur le composant ou dans le magasin, tel que loading = true
, puis à afficher un composant de chargement pendant que l'appel d'API se termine. Ensuite, nous définirions loading = false
et afficherons les données.
À la place, utilisons fetch
pour remplir le Store avant le rendu. Dans une nouvelle page appelée /products/view-async
, changeons la méthode created
en fetch
; ça devrait marcher non ?
export default { fetch () { // Unfortunately the below line throws an error // because 'this.$store' is undefined... this.$store.dispatch('product/load') }, computed: {...} }
Voici le hic : ces méthodes « suralimentées » s'exécutent avant la création du composant, donc this
ne pointe pas vers le composant et rien n'est accessible dessus. Alors, comment accéder au magasin ici ?
L'API de contexte
Bien sûr, il existe une solution. Sur toutes les méthodes de Nuxt, vous disposez d'un argument (normalement le premier) contenant un objet extrêmement utile appelé le Contexte. C'est tout ce dont vous aurez besoin dans l'application, ce qui signifie que nous n'avons pas besoin d'attendre que Vue crée d'abord ces références sur le composant.
Je vous recommande fortement de consulter la documentation contextuelle pour voir ce qui est disponible. Certains pratiques sont app
, où vous pouvez accéder à tous vos plugins, redirect
, qui peuvent être utilisés pour modifier les itinéraires, error
pour afficher la page d'erreur, et certains explicites tels que route
, query
et store
.
Ainsi, pour accéder au Store, on peut déstructurer le Contexte et en extraire le Store. Nous devons également nous assurer que nous renvoyons une promesse afin que Nuxt puisse attendre qu'elle soit résolue avant de rendre le composant. Nous devons donc également apporter un petit ajustement à notre action 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) }) }
Vous pouvez utiliser async/wait ou d'autres méthodes en fonction de votre style de codage, mais le concept est le même : nous demandons à Nuxt de s'assurer que l'appel d'API se termine et que le Store est mis à jour avec le résultat avant d'essayer de rendre le composant. Si vous essayez de naviguer vers /products/view-async
, vous ne verrez pas le flash de contenu où le produit est dans son état initial.
Vous pouvez imaginer à quel point cela peut être utile dans n'importe quelle application Vue, même sans SSR. Le contexte est également disponible pour tous les middlewares ainsi que pour d'autres méthodes Nuxt telles que NuxtServerInit
qui est une action de magasin spéciale qui s'exécute avant l'initialisation du magasin (un exemple de ceci est dans la section suivante)
Considérations lors de l'utilisation du SSR
Je suis sûr que beaucoup (moi y compris) qui commencent à utiliser une technologie telle que Nuxt tout en la traitant comme n'importe quel autre projet Vue finissent par se heurter à un mur où quelque chose que nous savons normalement fonctionner semble impossible dans Nuxt. Comme davantage de ces mises en garde sont documentées, il sera plus facile de les surmonter, mais la principale chose à considérer lors du démarrage du débogage est que le client et le serveur sont deux entités distinctes.
Lorsque vous accédez initialement à une page, une requête est envoyée à Nuxt, le serveur construit autant que possible cette page et le reste de l'application, puis le serveur vous l'envoie. Il incombe ensuite au client de poursuivre la navigation et de charger les morceaux selon ses besoins.
Nous voulons que le serveur en fasse le plus possible en premier, mais parfois il n'a pas accès aux informations dont il a besoin, ce qui fait que le travail est plutôt effectué côté client. Ou pire, lorsque le contenu final présenté par le client est différent de ce que le serveur attendait, le client est invité à le reconstruire à partir de zéro. C'est une grande indication que quelque chose ne va pas avec la logique de l'application. Heureusement, une erreur sera générée dans la console de votre navigateur (en mode développement) si cela commence à se produire.
Prenons un exemple de la façon de résoudre un problème courant, la gestion de session. Imaginez que vous ayez une application Vue où vous pouvez vous connecter à un compte, et votre session est stockée à l'aide d'un jeton (JWT, par exemple) que vous décidez de conserver dans localStorage
. Lorsque vous accédez initialement au site, vous souhaitez authentifier ce jeton par rapport à une API, qui renvoie des informations utilisateur de base si elles sont valides et place ces informations dans le Store.
Après avoir lu la documentation de Nuxt, vous voyez qu'il existe une méthode pratique appelée NuxtServerInit
qui vous permet de remplir de manière asynchrone le magasin une fois lors du chargement initial. Cela semble parfait ! Vous créez donc votre module utilisateur dans le Store et ajoutez l'action appropriée dans le fichier index.js
du répertoire Store :
export const actions = { nuxtServerInit ({ dispatch }) { // localStorage should work, right? const token = localStorage.getItem('token') if (token) return dispatch('user/load', token) } }
Lorsque vous actualisez la page, vous obtenez une erreur, localStorage is not defined
. En pensant à l'endroit où cela se produit, cela a du sens. Cette méthode est exécutée sur le serveur, elle n'a aucune idée de ce qui est stocké dans localStorage
sur le client ; en fait, il ne sait même pas ce qu'est « localStorage » ! Ce n'est donc pas une option.
Alors, quelle est la solution ? Il y en a quelques-uns, en fait. Vous pouvez demander au client d'initialiser le magasin à la place, mais finir par perdre les avantages de SSR car le client finit par faire tout le travail. Vous pouvez configurer des sessions sur le serveur, puis les utiliser pour authentifier l'utilisateur, mais c'est une autre couche à configurer. Ce qui ressemble le plus à la méthode localStorage
utilise des cookies à la place.
Nuxt a accès aux cookies car ils sont envoyés avec la requête du client au serveur. Comme pour les autres méthodes Nuxt, nuxtServerInit
a accès au Context, cette fois comme second argument car le premier est réservé au store. Sur le contexte, nous pouvons accéder à l'objet req
, qui stocke tous les en-têtes et autres informations de la demande du client. (Cela vous sera particulièrement familier si vous avez utilisé Node.js.)
Ainsi, après avoir stocké le jeton dans un cookie à la place (appelé "jeton", dans ce cas), accédons-y sur le serveur.
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) } }
Une solution simple, mais qui pourrait ne pas être immédiatement évidente. Apprendre à réfléchir à l'endroit où certaines actions se produisent (client, serveur ou les deux) et à quoi ils ont accès prend un certain temps, mais les avantages en valent la peine.
Déploiement
Le déploiement avec Nuxt est extrêmement simple. En utilisant la même base de code, vous pouvez créer une application SSR, une application monopage ou une page statique.
Application rendue côté serveur (application SSR)
C'est probablement ce que vous recherchiez en utilisant Nuxt. Le concept de base du déploiement ici consiste à exécuter le processus de build
sur la plate-forme que vous choisissez et à définir quelques configurations. Je vais utiliser l'exemple Heroku de la documentation :
Tout d'abord, configurez les scripts pour Heroku dans package.json
:
"scripts": { "dev": "nuxt", "build": "nuxt build", "start": "nuxt start", "heroku-postbuild": "npm run build" }
Ensuite, configurez l'environnement Heroku à l'aide de heroku-cli
(instructions de configuration ici :
# 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
C'est ça. Maintenant, votre application SSR Vue est prête à être vue par le monde. D'autres plates-formes ont des configurations différentes, mais le processus est similaire. Les méthodes de déploiement officielles actuellement répertoriées sont :
- À présent
- Dokku (océan numérique)
- Nginx
Application monopage (SPA)
Si vous souhaitez profiter de certaines des fonctionnalités supplémentaires fournies par Nuxt mais éviter que le serveur n'essaie de rendre les pages, vous pouvez alors déployer en tant que SPA à la place.
Tout d'abord, il est préférable de tester votre application sans le SSR, car par défaut npm run dev
s'exécute avec le SSR activé. Pour changer cela, éditez le fichier nuxt.config.js
et ajoutez l'option suivante :
mode: 'spa',
Désormais, lorsque vous exécutez npm run dev
, SSR sera désactivé et l'application s'exécutera en tant que SPA à tester. Ce paramètre garantit également qu'aucune future version n'inclura SSR.
Si tout semble correct, le déploiement est exactement le même que pour une application SSR. N'oubliez pas que vous devez d'abord définir mode: 'spa'
pour que le processus de construction sache que vous voulez un SPA.
Pages statiques
Si vous ne voulez pas du tout traiter avec un serveur et que vous souhaitez plutôt générer des pages à utiliser avec des services d'hébergement statiques tels que Surge ou Netlify, c'est l'option à choisir. Gardez simplement à l'esprit que, sans serveur, vous ne pourrez pas accéder aux req
et res
dans le contexte, donc si votre code repose sur cela, assurez-vous de l'adapter. Par exemple, lors de la génération de l'exemple de projet, la fonction nuxtServerInit
génère une erreur car elle tente de récupérer un jeton à partir des cookies dans les en-têtes de requête. Dans ce projet, cela n'a pas d'importance, car ces données ne sont utilisées nulle part, mais dans une application réelle, il faudrait un autre moyen d'accéder à ces données.
Une fois que c'est trié, le déploiement est facile. Une chose que vous devrez probablement changer en premier est l'ajout d'une option pour que la commande nuxt generate
crée également un fichier de secours. Ce fichier demandera au service d'hébergement de laisser Nuxt gérer le routage plutôt que le service d'hébergement, en renvoyant une erreur 404. Pour cela, ajoutez la ligne suivante à nuxt.config.js
:
generate: { fallback: true },
Voici un exemple utilisant Netlify, qui n'est pas actuellement dans la documentation Nuxt. N'oubliez pas que si c'est la première fois que vous utilisez netlify-cli
, vous serez invité à vous authentifier :
# install netlify-cli globally npm install netlify-cli -g # generate the application (outputs to dist/ folder) npm run generate # deploy netlify deploy dist
C'est aussi simple que ça! Comme mentionné au début de l'article, il existe une version de ce projet ici. Vous trouverez également ci-dessous une documentation officielle sur le déploiement des services suivants :
- Monter
- Pages GitHub
Apprendre encore plus
Nuxt se met à jour rapidement, et ce n'est qu'une petite sélection des fonctionnalités qu'il offre. J'espère que cet article vous encourage à l'essayer et à voir s'il peut améliorer les capacités de vos applications Vue, vous permettant de développer plus rapidement et de tirer parti de ses puissantes fonctionnalités.
Si vous cherchez plus d'informations, ne cherchez pas plus loin que les liens officiels de Nuxt :
- Documentation
- Playground
- GitHub
- FAQ
Looking to up your JavaScript game? Try reading The Comprehensive Guide to JavaScript Design Patterns by fellow Toptaler Marko Mišura.