Comment configurer l'installation des variables d'environnement Nuxt.js en runtime, ou Comment tout faire n'aime pas tout le monde et ne le regrette pas


( Illustration )

Les développeurs Web seniors Anton et Alexei poursuivent l'histoire de la lutte difficile avec Nuxt. Lors de la précédente manche de la bataille avec ce framework, ils ont montré comment lancer un projet sur Nuxt pour que tout le monde soit content. Dans un nouvel article, nous parlerons de l'application réelle du cadre.

Nous avons commencé à réécrire le projet avec une énorme dette technique. L'audience mensuelle était de 6 à 7 millions de visiteurs uniques, mais la plateforme existante a causé trop de problèmes. Il a donc été décidé de l'envoyer à la retraite. Bien sûr, la performance était notre plus grande préoccupation, mais je ne voulais pas non plus gaspiller le référencement.

Après quelques tours de discussion, ils ont décidé de ne pas s'appuyer sur l'approche traditionnelle avec uniquement le rendu côté serveur - mais de ne pas se piéger dans le rendu côté client. En conséquence, nous avons commencé à construire une solution basée sur Nuxt.js.

Bon vieux Nuxt.js


Nous prenons le «framework for framework» basé sur Vue.js, déjà connu de nous dans le dernier article, pour construire des applications client-serveur universelles. Dans notre cas, l'application fonctionne en conjonction avec une API assez compliquée (les subtilités des microservices, mais à peu près à un autre moment) et plusieurs couches de mise en cache, elle rend le contenu modifiable par les éditeurs et renvoie du contenu déjà statique pour des performances ultra-rapides. Super, non?

En fait, il n'y a rien de nouveau ici. Mais ce qui rend Nuxt.js intéressant, c'est la possibilité de démarrer rapidement un projet avec un rendu client-serveur. Parfois, vous devez aller à l'encontre du cadre établi du cadre. C'est ce que nous avons fait.

Pas le temps d'expliquer, de construire une fois, d'en déployer plusieurs!


Un jour, un techlide s'est approché de nous et a laissé perplexe: chaque fois que nous poussons les modifications vers le référentiel, nous devons créer chacun des environnements (environnements de développement, de scène et de production) séparément. C'était lent. Mais quelle est la différence entre ces versions? Oui, uniquement dans les variables d'environnement! Et ce qu'il a demandé de faire semblait logique et raisonnable. Mais notre première réaction a été: O_o

Le Build une fois, déployer de nombreuses stratégies est logique dans le monde du développement logiciel. Mais dans le monde Javascript ... Nous avons toute une batterie de compilateurs, transpilers, pré et post-processeurs, ainsi que des tests et des linters. Tout cela prend du temps pour les configurer pour chacun des environnements. De plus, il existe de nombreux problèmes potentiels de fuite de données confidentielles (secrets, clés API, etc. qui peuvent être stockés dans des configurations).

Et nous avons commencé


Bien sûr, nous avons commencé par une recherche sur Google. Ensuite, nous avons parlé avec les mainteneurs de Nuxt.js, mais sans grand succès. Que faire - j'ai dû trouver une solution par moi-même et ne pas la copier à partir de StackOverflow (c'est la base de notre activité, non?).

Voyons comment Nuxt.js le fait.


Nuxt.js possède un fichier de configuration avec le nom attendu nuxt.config.js. Il est utilisé pour transférer par programme des configurations à l'application:

const config = require('nuxt.config.js') const nuxt = new Nuxt(config) 

Il est possible de définir l'environnement via des variables env. En général, une pratique assez courante consiste à connecter dynamiquement le fichier de configuration. Ensuite, tout cela est transféré vers le webpack definePlugin et peut être utilisé sur le client et le serveur, quelque chose comme ceci:

process.env.propertyName
// ou
context.env.propertyName.

Ces variables sont cuites lors de l'assemblage, plus d'informations ici: page env Nuxt.js.
Avez-vous remarqué le webpack? Oui, cela signifie compilation, et ce n'est pas ce que nous voulons.

Essayons différemment


Comprendre comment fonctionne Nuxt.js signifie pour nous:

  • nous ne pouvons plus utiliser env dans nuxt.config.js;
  • toutes les autres variables dynamiques (par exemple, dans head.meta) doivent être transmises à l'objet nuxt.config.js lors de l'exécution.


Code dans server / index.js:

 const config = require('../nuxt.config.js') 

Changer pour:

 //    Nuxt.js const config = require('./utils/extendedNuxtConfig.js').default 

Où utils / extendedNuxtConfig.js:

 import config from 'config' import get from 'lodash/get' //   Nuxt.js const defaultConfig = require('../../nuxt.config.js') //   const extendedConfig = {} //   Nuxt.js const nuxtConfig = { ...defaultConfig, ...extendedConfig } //     //       if (get(nuxtConfig, 'head.meta')) { nuxtConfig.head.meta.push({ hid: 'og:url', property: 'og:url', content: config.get('app.canonical_domain') }) } export default nuxtConfig 

Nous n'avons même pas remarqué l'éléphant


Eh bien, nous avons résolu le problème de l'obtention de variables dynamiques à l'extérieur de la propriété env de l'objet de configuration dans nuxt.config.js. Mais le problème d'origine n'est toujours pas résolu.

Il y avait une hypothèse que certains abstractEnv.js abstraits seraient utilisés pour:

  • client - créer un fichier env.js qui sera téléchargé globalement (window.env.envKey),
  • serveur - est importé dans les modules, si nécessaire,
  • code isomorphe, quelque chose comme
    context.isClient? window.env [clé]: global.sharedEnv [clé].

D'une manière ou d'une autre pas génial. Cette abstraction résoudrait le problème le plus grave - la fuite de données confidentielles dans l'application cliente, car il serait nécessaire d'ajouter la valeur consciemment.

Vuex nous aidera


En étudiant le problème, nous avons remarqué que le magasin Vuex est exporté vers un objet fenêtre. Cette solution est forcée de supporter l'isomorphisme de Nuxt, js. Vuex est un entrepôt de données inspiré de Flux spécialement conçu pour les applications Vue.js.

Eh bien, pourquoi ne pas l'utiliser pour nos variables partagées? Il s'agit d'une approche plus organique - les données dans un référentiel mondial nous conviennent.

Commençons par server / utils / sharedEnv.js:

 import config from 'config' /** *  ,      *  ,     *  ,       * * @type {Object} */ const sharedEnv = { // ... canonicalDomain: config.get('app.canonical_domain'), } export default sharedEnv 

Le code ci-dessus s'exécutera lors du démarrage du serveur. Ajoutez-le ensuite au référentiel Vuex:

 /** *   . *        * .   * https://nuxtjs.org/guide/vuex-store/#the-nuxtserverinit-action * * @return {Object} Shared environment variables. */ const getSharedEnv = () => process.server ? require('~/server/utils/sharedEnv').default || {} : {} // ... export const state = () => ({ // ... sharedEnv: {} }) export const mutations = { // ... setSharedEnv (state, content) { state.sharedEnv = content } } export const actions = { nuxtServerInit ({ commit }) { if (process.server) { commit('setSharedEnv', getSharedEnv()) } } } 

Nous nous baserons sur le fait que nuxtServerInit est lancé pendant, hmm, l'initialisation du serveur. Il y a une difficulté: faites attention à la méthode getSharedEnv, ici vérifiez que l'exécution répétée sur le serveur est ajoutée.

Qu'est-il arrivé?


Nous avons maintenant des variables communes qui peuvent être extraites dans des composants comme celui-ci:
this. $ store.state.sharedEnv.canonicalDomain

Victoire!

Oh non. Et les plugins?


Certains plugins nécessitent des variables d'environnement pour être configurés. Et quand on veut les utiliser:
Vue.use (MyPlugin, {someEnvOption: 'Il n'y a pas d'accès au magasin vuex'})

Grande condition de concurrence, Vue.js essaie de s'initialiser avant que Nuxt.js enregistre sharedEnvobject dans le référentiel Vuex.

Bien que la fonction qui enregistre les plugins donne accès à l'objet Context contenant la référence du référentiel, sharedEnv est toujours vide. Ceci est résolu assez simplement - faisons du plugin une fonction asynchrone et attendons que nuxtServerInit s'exécute:

 import Vue from 'vue' import MyPlugin from 'my-plugin' /** *   MyPlugin . */ export default async (context) => { //  ,      sharedEnv await context.store.dispatch('nuxtServerInit', context) const env = { ...context.store.state.sharedEnv } Vue.use(MyPlugin, { option: env.someKey }) } 

C'est maintenant la victoire.

Source: https://habr.com/ru/post/fr429756/


All Articles