Cómo configurar la instalación de las variables de entorno Nuxt.js en tiempo de ejecución, o Cómo hacer que todo no le guste a todos y no arrepentirse


( Ilustración )

Los desarrolladores web senior Anton y Alexei continúan la historia de la difícil lucha con Nuxt. En la ronda anterior de la batalla con este marco, mostraron cómo lanzar un proyecto en Nuxt para que todos estuvieran felices. En un nuevo artículo hablaremos sobre la aplicación real del marco.

Comenzamos a reescribir el proyecto con una enorme deuda técnica. La audiencia mensual fue de 6-7 millones de visitantes únicos, pero la plataforma existente causó demasiados problemas. Por lo tanto, se decidió enviarla a la jubilación. Por supuesto, el rendimiento era nuestra mayor preocupación, pero tampoco quería desperdiciar el SEO.

Después de un par de rondas de discusión, decidieron no confiar en el enfoque tradicional con solo la representación del lado del servidor, sino no atraparse en la representación del lado del cliente. Como resultado, comenzamos a construir una solución basada en Nuxt.js.

El viejo Nuxt.js


Tomamos el "marco para el marco" basado en Vue.js, que ya conocemos en el último artículo, para construir aplicaciones universales cliente-servidor. En nuestro caso, la aplicación funciona junto con una API bastante complicada (las complejidades de los microservicios, pero sobre eso en otro momento) y varias capas de almacenamiento en caché, hace que el contenido sea editable por los editores y devuelve contenido ya estático para un rendimiento increíblemente rápido. Genial, verdad?

De hecho, no hay nada nuevo aquí. Pero lo que hace que Nuxt.js sea interesante es la capacidad de iniciar rápidamente un proyecto con representación cliente-servidor. A veces es necesario ir en contra del marco establecido del marco. Eso es lo que hicimos.

¡No hay tiempo para explicar, construir una vez, desplegar muchos!


Un día, un techlide se nos acercó y desconcertó: cada vez que enviamos cambios al repositorio, necesitamos construir cada uno de los entornos (entornos de desarrollo, etapa y producción) por separado. Fue lento Pero, ¿cuál es la diferencia entre estas compilaciones? ¡Sí, solo en variables de entorno! Y lo que pidió hacer sonaba lógico y razonable. Pero nuestra primera reacción fue: O_o

Construir una vez, implementar muchas estrategias tiene sentido en el mundo del desarrollo de software. Pero en el mundo de Javascript ... Tenemos una batería completa de compiladores, transpiladores, pre y postprocesadores, así como pruebas y linters. Todo esto lleva tiempo configurarlos para cada uno de los entornos. Además, hay muchos problemas potenciales de fuga de datos confidenciales (secretos, claves API, etc. que pueden almacenarse en configuraciones).

Y empezamos


Por supuesto, comenzamos con una búsqueda en Google. Luego hablamos con los mantenedores de Nuxt.js, pero sin mucho éxito. Qué hacer: tuve que encontrar una solución por mi cuenta y no copiarla de StackOverflow (esta es la base de nuestra actividad, ¿verdad?).

Veamos cómo lo hace Nuxt.js.


Nuxt.js tiene un archivo de configuración con el nombre esperado nuxt.config.js. Se utiliza para transferir configuraciones mediante programación a la aplicación:

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

Es posible establecer el entorno a través de variables env. En general, una práctica bastante común es conectar el archivo de configuración dinámicamente. Luego, todo esto se transfiere al paquete web definePlugin y se puede usar en el cliente y el servidor, algo así:

process.env.propertyName
// o
context.env.propertyName.

Estas variables se hornean durante el ensamblaje, más información aquí: Nuxt.js env page .
¿Has notado el paquete web? Sí, eso significa compilación, y eso no es lo que queremos.

Probemos diferente


Comprender cómo funciona Nuxt.js significa para nosotros:

  • ya no podemos usar env dentro de nuxt.config.js;
  • cualquier otra variable dinámica (por ejemplo, dentro de head.meta) debe pasarse al objeto nuxt.config.js en tiempo de ejecución.


Código en server / index.js:

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

Cambiar a:

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

Donde 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 

Ni siquiera nos dimos cuenta del elefante


Bueno, resolvimos el problema de obtener variables dinámicas desde fuera de la propiedad env del objeto de configuración en nuxt.config.js. Pero el problema original aún no se resuelve.

Se suponía que se utilizarían algunos abstract sharedEnv.js para:

  • cliente: cree un archivo env.js que se descargará globalmente (window.env.envKey),
  • servidor: se importa a los módulos, cuando sea necesario,
  • código isomorfo, algo así como
    context.isClient? window.env [clave]: global.sharedEnv [clave].

De alguna manera no es genial. Esta abstracción resolvería el problema más grave: la fuga de datos confidenciales en la aplicación del cliente, ya que sería necesario agregar el valor conscientemente.

Vuex nos ayudará


Mientras investigábamos el problema, notamos que la tienda Vuex se exporta a un objeto de ventana. Esta solución se ve obligada a soportar el isomorfismo de Nuxt, js. Vuex es un almacén de datos inspirado en Flux diseñado específicamente para aplicaciones Vue.js.

Bueno, ¿por qué no usarlo para nuestras variables compartidas? Este es un enfoque más orgánico: los datos en un repositorio global nos convienen.

Comencemos con server / utils / sharedEnv.js:

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

El código anterior se ejecutará durante el inicio del servidor. Luego agréguelo al repositorio de 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()) } } } 

Confiaremos en el hecho de que nuxtServerInit se inicia durante, hmm, la inicialización del servidor. Hay algunas dificultades: preste atención al método getSharedEnv, aquí verifique si se agrega la ejecución repetida en el servidor.

Que paso


Ahora tenemos variables comunes que se pueden extraer en componentes como este:
this. $ store.state.sharedEnv.canonicalDomain

Victoria!

Oh no ¿Qué pasa con los complementos?


Algunos complementos requieren variables de entorno para configurar. Y cuando queremos usarlos:
Vue.use (MyPlugin, {someEnvOption: 'No hay acceso a la tienda vuex'})

Excelente condición de carrera, Vue.js intenta inicializarse antes de que Nuxt.js registre sharedEnvobject en el repositorio de Vuex.

Aunque la función que registra complementos proporciona acceso al objeto Context que contiene el enlace del repositorio, sharedEnv todavía está vacío. Esto se resuelve de manera bastante simple: hagamos que el complemento sea una función asíncrona y esperemos a que se ejecute nuxtServerInit:

 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 }) } 

Ahora es la victoria.

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


All Articles