Передать переменную среды в приложение Vue во время выполнения

Как я могу получить доступ к переменным среды в Vue, которые передаются в контейнер во время выполнения, а не во время сборки?

Стек выглядит следующим образом:

  • VueCLI 3.0.5
  • Докер
  • Kubernetes

Существуют предлагаемые решения для stackoverflow и в других местах для использования файла .env для передачи переменных (и использования режима), но это во время сборки и запекается в образе докера.

Я хотел бы передать переменную во Vue во время выполнения следующим образом:

  • Создайте Kubernetes ConfigMap (я правильно понимаю)
  • Передайте значение ConfigMap в переменную env K8s pod при запуске файла yaml развертывания (я понимаю)
  • Чтение из переменной env, созданной выше, например. VUE_APP_MyURL и сделайте что-нибудь с этим значением в моем приложении Vue (я НЕ понимаю)

Я пробовал следующее в helloworld.vue:

<template>
<div>{{displayURL}}
  <p>Hello World</p>
</div>
</template>
<script>
export default {  
    data() {
        return {
            displayURL: ""
        }
    },
    mounted() {
        console.info("check 1")
        this.displayURL=process.env.VUE_APP_ENV_MyURL
        console.info(process.env.VUE_APP_ENV_MyURL)
        console.info("check 3")
    }
}
</script>

Я получаю "undefined" в журнале консоли, а на странице helloworld ничего не отображается.

Я также пробовал передать значение в файл vue.config и прочитать его оттуда. Тот же результат "undefined" в console.info

<template>
<div>{{displayURL}}
  <p>Hello World</p>
</div>
</template>
<script>
const vueconfig = require('../../vue.config');
export default {  
    data() {
        return {
            displayURL: ""
        }
    },
    mounted() {
        console.info("check 1")
        this.displayURL=vueconfig.VUE_APP_MyURL
        console.info(vueconfig.VUE_APP_MyURL)
        console.info("check 3")
    }
}
</script>

Когда vue.config выглядит так:

module.exports = {
    VUE_APP_MyURL: process.env.VUE_APP_ENV_MyURL
}

Если я жестко закодирую значение в VUE_APP_MyURL в файле vue.config, оно будет успешно отображаться на странице helloworld.

VUE_APP_ENV_MyURL успешно заполняется правильным значением, когда я его опрашиваю: kubectl describe pod

process.env.VUE_APP_MyURL, похоже, не может успешно получить значение.

Для чего это стоит ... Я могу успешно использовать process.env.VUE_APP_3rdURL для передачи значений в приложение Node.js во время выполнения.

Вы встраиваете код vue в статический html / js или запускаете его как dev в докере?

Varcorb 26.10.2018 16:00

FWIW, если у вас есть доступ к файлу, содержащему переменные env, вы можете использовать FileReader API для его анализа

Simone 26.10.2018 17:14

Спасибо @varcorb, я встраиваю код vue в статический html / js

Johann 29.10.2018 09:37

в соответствии с тем, что вы спрашиваете, ответ Хендрика Малкоу делает это, и было очень полезно, что он подробно рассказал, как это добавить. Пожалуйста, отметьте это как правильный ответ! Чтобы быть ясным, у меня есть конфигурационная карта k8s, которая вводится во время выполнения и позволяет мне передавать базовый URL-адрес поверх URL-адреса из process.env

Evan Morrison 01.02.2020 02:15
Развертывание модели машинного обучения с помощью Flask - Angular в Kubernetes
Развертывание модели машинного обучения с помощью Flask - Angular в Kubernetes
Kubernetes - это портативная, расширяемая платформа с открытым исходным кодом для управления контейнерными рабочими нагрузками и сервисами, которая...
Как создать PHP Image с нуля
Как создать PHP Image с нуля
Сегодня мы создадим PHP Image from Scratch для того, чтобы легко развернуть базовые PHP-приложения. Пожалуйста, имейте в виду, что это разработка для...
35
4
20 418
5
Перейти к ответу Данный вопрос помечен как решенный

Ответы 5

У меня была такая же проблема в моем текущем проекте, и я обнаружил, что в настоящий момент невозможно получить доступ к переменным среды во время выполнения, поэтому я получаю решение создания файлов .env или локальных переменных среды, которые, как вы сказали, используются во время сборки.

Автор этого сообщения объясняет, что нет смысла пытаться передать переменную в статический код html.js. Наконец-то я понял! reddit.com/r/vuejs/comments/8sbosc/…

Johann 29.10.2018 15:43
Ответ принят как подходящий

Создайте файл config.js с желаемой конфигурацией. Мы будем использовать это позже, чтобы создать карту конфигурации, которую мы развернем в Kubernetes. Поместите его в свой проект Vue.js, где находятся другие ваши файлы JavaScript. Хотя позже мы исключим его из минификации, полезно иметь его там, чтобы инструменты IDE работали с ним.

const config = (() => {
  return {
    "VUE_APP_ENV_MyURL": "...",
  };
})();

Теперь убедитесь, что ваш скрипт исключен из минификации. Для этого создайте файл vue.config.js со следующим содержимым, в котором хранится наш файл конфигурации.

const path = require("path");
module.exports = {
  publicPath: '/',
  configureWebpack: {
    module: {
      rules: [
        {
          test: /config.*config\.js$/,
          use: [
            {
              loader: 'file-loader',
              options: {
                name: 'config.js'
              },
            }
          ]
        }
      ]
    }
  }
}

В свой index.html добавьте блок сценария для загрузки файла конфигурации вручную. Обратите внимание, что конфигурационного файла там не будет, поскольку мы его только что исключили. Позже мы смонтируем его с ConfigMap в наш контейнер. В этом примере мы предполагаем, что монтируем его в тот же каталог, что и наш HTML-документ.

<script src = "<%= BASE_URL %>config.js"></script>

Измените свой код, чтобы использовать нашу конфигурацию времени выполнения:

this.displayURL = config.VUE_APP_ENV_MyURL || process.env.VUE_APP_ENV_MyURL 

В Kubernetes создайте карту конфигурации, которая использует содержимое вашего файла конфигурации. Конечно, вы хотите прочитать содержимое вашего файла конфигурации.

apiVersion: v1
kind: ConfigMap
metadata:
  ...
data:
  config.js: |
    var config = (() => {
      return {
        "VUE_APP_ENV_MyURL": "...",
      };
    })();

Ссылка на карту конфигурации в вашем развертывании. Это монтирует конфигурационную карту как файл в ваш контейнер. mountPath уже содержит наш уменьшенный index.html. Мы монтируем файл конфигурации, на который мы ссылались ранее.

apiVersion: apps/v1
kind: Deployment
metadata:
  ...
spec:
  ...
  template:
    ...
    spec:
      volumes:
        - name: config-volume
          configMap:
            name: ...
      containers:
        - ...
          volumeMounts:
                - name: config-volume
                  mountPath: /usr/share/nginx/html/config.js
                  subPath: config.js

Теперь вы можете получить доступ к файлу конфигурации на <Base URL>/config.js, и вы должны увидеть точное содержимое, которое вы поместили в запись ConfigMap. Ваш HTML-документ загружает эту конфигурационную карту по мере загрузки остальной части вашего миниатюрного кода Vue.js. Вуаля!

где мне разместить config.js и как импортировать конфигурацию, когда я использую ее в коде?

NehaM 12.09.2019 15:20

Я просто добавил более подробное объяснение. это помогает?

Hendrik M Halkow 16.10.2019 03:25

@HendrikMHalkow Не могли бы вы добавить tree того, как выглядит структура вашего проекта?

Aaron N. Brock 09.09.2020 01:21

Привет, это решение работает, если объект config необходимо указать в файле Typescript? Спасибо.

Frank Liu 13.04.2021 09:11

Я добавляю сюда свой рабочий раствор, для тех, у кого все еще возникают проблемы. Я действительно думаю, что ответ @Hendrik M Halkow более элегантен, хотя мне не удалось решить его с помощью этого просто из-за отсутствия у меня опыта в webpack и Vue. Я просто не мог понять, где разместить конфигурационный файл и как его использовать.

Мой подход состоит в том, чтобы использовать переменные среды с константами (фиктивные значения), чтобы создать его для production, а затем заменить эти константы в образе с помощью специального сценария entrypoint. Решение выглядит так.

Я инкапсулировал все конфиги в один файл под названием app.config.js

export const clientId = process.env.VUE_APP_CLIENT_ID
export const baseURL = process.env.VUE_APP_API_BASE_URL

export default {
  clientId,
  baseURL,
}

Это используется в проекте просто путем поиска значения в файле конфигурации.

import { baseURL } from '@/app.config';

Затем я использую стандартные файлы .env. [профиль] для установки переменных среды. например .env.development

VUE_APP_API_BASE_URL=http://localhost:8085/radar-upload
VUE_APP_CLIENT_ID=test-client

Затем для производство я устанавливаю строковые константы как значения. например .env.production

VUE_APP_API_BASE_URL=VUE_APP_API_BASE_URL
VUE_APP_CLIENT_ID=VUE_APP_CLIENT_ID

Пожалуйста, не здесь, значение может быть любой уникальной строкой. Чтобы упростить чтение, я просто заменяю имя переменной среды в качестве значения. Он будет просто скомпилирован и объединен, как в режиме разработки.

В моем Dockerfile я добавляю entrypoint, который может читать эти константы и заменять его значениями переменных среды.

Мой Dockerfile выглядит так (это довольно стандартно)

FROM node:10.16.3-alpine as builder

RUN mkdir /app
WORKDIR /app

COPY package*.json /app/
RUN npm install

COPY . /app/

RUN npm run build --prod

FROM nginx:1.17.3-alpine

# add init script
COPY ./docker/nginx.conf /etc/nginx/nginx.conf

WORKDIR /usr/share/nginx/html

COPY --from=builder /app/dist/ .

COPY ./docker/entrypoint.sh /entrypoint.sh

# expose internal port:80 and run init.sh
EXPOSE 80

ENTRYPOINT ["/entrypoint.sh"]
CMD ["nginx", "-g", "daemon off;"]

Затем создайте файл ./docker/entrypoint.sh, как показано ниже.

#!/bin/sh

ROOT_DIR=/usr/share/nginx/html

# Replace env vars in JavaScript files
echo "Replacing env constants in JS"
for file in $ROOT_DIR/js/app.*.js* $ROOT_DIR/index.html $ROOT_DIR/precache-manifest*.js;
do
  echo "Processing $file ...";

  sed -i 's|VUE_APP_API_BASE_URL|'${VUE_APP_API_BASE_URL}'|g' $file 
  sed -i 's|VUE_APP_CLIENT_ID|'${VUE_APP_CLIENT_ID}'|g' $file

done

echo "Starting Nginx"
nginx -g 'daemon off;'

Это позволяет мне иметь настраиваемый образ во время выполнения, который я могу запускать во многих средах. Я знаю, что это что-то вроде взлома. Но видел, как многие люди поступают так.

Надеюсь, это кому-то поможет.

Привет, @NehaM, я только что добавил более подробное объяснение к предлагаемому мной решению. Надеюсь, это вам поможет. Я рекомендую избегать написания сценариев для вашей точки входа, поскольку это не работает с образами без дистрибутива. Взгляните на github.com/SDA-SE/nginx - изображение NGINX размером менее 6 МБ. Если вы не можете избежать написания сценариев, поместите exec перед последней командой, чтобы процесс оболочки был заменен вашим процессом nginx.

Hendrik M Halkow 16.10.2019 03:31

Спасибо за предложение и за импровизированный ответ. Я все еще использую решение, которое придумал. Я добавил небольшие улучшения в код.

NehaM 17.10.2019 17:04

Я заставил его работать с решением, предложенным @Hendrik M Halkow.

Но я сохранил config.js в статической папке. Поступая так, мне не нужно заботиться о том, чтобы файл не уменьшился.

Затем включите это так:

<script src = "<%= BASE_URL %>static/config.js"></script>

и используйте эту конфигурацию монтирования тома:

...
volumeMounts:
    - name: config-volume
      mountPath: /usr/share/nginx/html/static/config.js
      subPath: config.js

Создать файл конфигурации

В общей папке: общедоступный / config.js

const config = (() => {
  return {
    "VUE_CONFIG_APP_API": "...",
  };
})();

Обновите index.html

Обновите общедоступный / index.html, чтобы в конце заголовка содержалось следующее:

  <!-- docker configurable variables -->
  <script src = "<%= BASE_URL %>config.js"></script>

Нет необходимости обновлять vue.config.js, поскольку мы используем общую папку для конфигурации.

ESLint

ESLint выдал бы нам ошибку использования неопределенной переменной. Поэтому мы определяем глобальную переменную в файле .eslintrc.js:

  globals: {
    config: "readable",
  },

использование

Например. в магазине src / store / user.js

export const actions = {
  async LoadUsers({ dispatch }) {
    return await dispatch(
      "axios/get",
      {
        url: config.VUE_CONFIG_APP_API + "User/List",
      },
      { root: true }
    );
  },
...

K8S ConfigMap:

apiVersion: v1
kind: ConfigMap
metadata:
  name: fe-config
  namespace: ...
data:
  config.js: |
    var config = (() => {
      return {
        "VUE_CONFIG_APP_API": "...",
      };
    })();

Развертывание

apiVersion: apps/v1
kind: Deployment
metadata:
  ...
spec:
  ...
  template:
    ...
    spec:
      volumes:
        - name: config-volume
          configMap:
            name: fe-config
      containers:
        - ...
          volumeMounts:
                - name: config-volume
                  mountPath: /usr/share/nginx/html/config.js
                  subPath: config.js

Другие вопросы по теме