Como configurar a instalação das variáveis ​​de ambiente Nuxt.js em tempo de execução ou Como fazer tudo que não é de todo mundo e não se arrepender


( Ilustração )

Os desenvolvedores sênior da web Anton e Alexei continuam a história da difícil luta com o Nuxt. Na rodada anterior da batalha contra essa estrutura, eles mostraram como iniciar um projeto no Nuxt para que todos ficassem felizes. Em um novo artigo, falaremos sobre a aplicação real do framework.

Começamos a reescrever o projeto com uma enorme dívida técnica. O público mensal era de 6 a 7 milhões de visitantes únicos, mas a plataforma existente causou muitos problemas. Portanto, decidiu-se mandá-la para a aposentadoria. Obviamente, o desempenho era a nossa maior preocupação, mas eu também não queria desperdiçar SEO.

Após algumas rodadas de discussão, eles decidiram não confiar na abordagem tradicional apenas com a renderização do lado do servidor - mas não se prender na renderização do lado do cliente. Como resultado, começamos a criar uma solução baseada no Nuxt.js.

Bom velho Nuxt.js


Adotamos a “estrutura para estrutura” baseada no Vue.js, já conhecida no último artigo, para a criação de aplicativos cliente-servidor universais. No nosso caso, o aplicativo trabalha em conjunto com uma API bastante complicada (os meandros dos microsserviços, mas sobre isso em outro momento) e várias camadas de cache, torna o conteúdo editável pelos editores e retorna o conteúdo já estático para um desempenho extremamente rápido. Ótimo, certo?

De fato, não há nada de novo aqui. Mas o que torna o Nuxt.js interessante é a capacidade de iniciar rapidamente um projeto com a renderização cliente-servidor. Às vezes você precisa ir contra a estrutura estabelecida da estrutura. Foi o que fizemos.

Não há tempo para explicar, crie uma vez, implante muitos!


Um dia, um techlide se aproximou de nós e ficou intrigado: sempre que fazemos alterações no repositório, precisamos construir cada um dos ambientes (ambientes de desenvolvimento, palco e prod) separadamente. Foi lento. Mas qual é a diferença entre essas construções? Sim, apenas em variáveis ​​de ambiente! E o que ele pediu para fazer parecia lógico e razoável. Mas nossa primeira reação foi: O_o

A estratégia Build, uma vez implantada, faz sentido no mundo do desenvolvimento de software. Mas no mundo do Javascript ... Temos uma bateria inteira de compiladores, transpilers, pré e pós-processadores, além de testes e linters. Tudo isso leva tempo para configurá-los para cada um dos ambientes. Além disso, existem muitos problemas em potencial de vazamento de dados confidenciais (segredos, chaves de API etc.) que podem ser armazenados em configurações.

E começamos


Obviamente, começamos com uma pesquisa no Google. Depois conversamos com os mantenedores do Nuxt.js, mas sem muito sucesso. O que fazer - eu tive que criar uma solução sozinha e não copiá-la do StackOverflow (essa é a base da nossa atividade, certo?).

Vamos descobrir como o Nuxt.js faz isso.


O Nuxt.js possui um arquivo de configuração com o nome esperado nuxt.config.js. É usado para transferir programaticamente configurações para o aplicativo:

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

É possível definir o ambiente através de variáveis ​​env. Em geral, uma prática bastante comum é conectar o arquivo de configuração dinamicamente. Então tudo isso é transferido para o webpack definePlugin e pode ser usado no cliente e no servidor, algo como isto:

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

Essas variáveis ​​são ativadas durante a montagem, mais informações aqui: página env do Nuxt.js.
Você notou o webpack? Sim, isso significa compilação, e não é isso que queremos.

Vamos tentar de forma diferente


Entender como o Nuxt.js funciona significa para nós:

  • não podemos mais usar o env dentro do nuxt.config.js;
  • quaisquer outras variáveis ​​dinâmicas (por exemplo, dentro de head.meta) devem ser passadas para o objeto nuxt.config.js em tempo de execução.


Código no server / index.js:

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

Mude para:

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

Onde 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 

Nós nem percebemos o elefante


Bem, resolvemos o problema de obter variáveis ​​dinâmicas fora da propriedade env do objeto de configuração no nuxt.config.js. Mas o problema original ainda não está resolvido.

Havia uma suposição de que algum resumo do sharedEnv.js seria usado para:

  • cliente - crie um arquivo env.js que será baixado globalmente (window.env.envKey),
  • servidor - é importado para os módulos, quando necessário,
  • código isomórfico, algo como
    context.isClient? window.env [chave]: global.sharedEnv [chave].

De alguma forma, não é ótimo. Essa abstração resolveria o problema mais sério - o vazamento de dados confidenciais no aplicativo cliente, pois seria necessário agregar o valor conscientemente.

A Vuex nos ajudará


Ao investigar o problema, percebemos que o repositório Vuex é exportado para um objeto de janela. Essa solução é forçada a suportar o isomorfismo de Nuxt, js. O Vuex é um data warehouse inspirado no Flux, projetado especificamente para aplicativos Vue.js.

Bem, por que não usá-lo para nossas variáveis ​​compartilhadas? Essa é uma abordagem mais orgânica - os dados em um repositório global nos convêm.

Vamos começar com server / utils / sharedEnv.js:

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

O código acima será executado durante a inicialização do servidor. Em seguida, adicione-o ao repositório 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()) } } } 

Vamos confiar no fato de que o nuxtServerInit é iniciado durante, hmm, a inicialização do servidor. Há alguma dificuldade: preste atenção ao método getSharedEnv, aqui verifique se a execução repetida no servidor foi adicionada.

O que aconteceu


Agora temos variáveis ​​comuns que podem ser extraídas em componentes como este:
this. $ store.state.sharedEnv.canonicalDomain

Vitória!

Oh não. E os plugins?


Alguns plug-ins requerem variáveis ​​de ambiente para serem configurados. E quando queremos usá-los:
Vue.use (MyPlugin, {someEnvOption: 'Não há acesso ao repositório vuex'})

Ótimo, condição de corrida, o Vue.js tenta se inicializar antes que o Nuxt.js registre sharedEnvobject no repositório do Vuex.

Embora a função que registra plug-ins forneça acesso ao objeto Context que contém o link do repositório, sharedEnv ainda está vazio. Isso é resolvido de maneira simples - vamos tornar o plug-in uma função assíncrona e aguardar a execução do 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 }) } 

Agora é a vitória.

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


All Articles