在运行时将环境变量传递给 Vue 应用程序

Posted

技术标签:

【中文标题】在运行时将环境变量传递给 Vue 应用程序【英文标题】:Pass environment variable into a Vue App at runtime 【发布时间】:2019-03-31 07:02:14 【问题描述】:

如何访问 Vue 中的环境变量,这些变量在运行时而不是在构建期间传递给容器?

堆栈如下:

VueCLI 3.0.5 码头工人 Kubernetes

在 *** 和其他地方有建议的解决方案来使用 .env 文件来传递变量(和使用模式),但这是在构建时并被烘焙到 docker 映像中。

我想在运行时将变量传递给 Vue,如下所示:

创建 Kubernetes ConfigMap(我猜对了) 在运行部署 yaml 文件时将 ConfigMap 值传递给 K8s pod env 变量(我猜对了) 从上面创建的环境变量中读取,例如。 VUE_APP_MyURL 并在我的 Vue 应用程序中使用该值做一些事情(我做错了)

我在 helloworld.vue 中尝试了以下内容:

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

</script>

我在控制台日志中返回“未定义”,而 helloworld 页面上没有显示任何内容。

我还尝试将值传递到 vue.config 文件并从那里读取它。在 console.log 中出现相同的“未定义”结果

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

</script>

vue.config 看起来像这样:

module.exports = 
    VUE_APP_MyURL: process.env.VUE_APP_ENV_MyURL

如果我将一个值硬编码到 vue.config 文件中的 VUE_APP_MyURL 中,它会成功显示在 helloworld 页面上。

当我询问 VUE_APP_ENV_MyURL 时,它已成功填充正确的值:kubectl describe pod

process.env.VUE_APP_MyURL 似乎没有成功检索到值。

为了它的价值...我能够在运行时成功使用 process.env.VUE_APP_3rdURL 将值传递到 Node.js 应用程序中。

【问题讨论】:

您将 vue 代码构建到静态 html/js 中还是在 docker 中将其作为 dev 运行? FWIW 只要你有权访问包含环境变量的文件,你就可以使用FileReader API 来解析它 感谢 @varcorb 我正在将 vue 代码构建到静态 html/js 中 根据您要问的确切内容,Hendrik Malkows 的回答是这样做的,他详细说明了如何添加它非常有用。请考虑将其标记为正确答案!需要明确的是,我有一个 k8s configMap,它在运行时被注入,并允许我从 process.env 的顶部传递一个基本 URL 【参考方案1】:

我在当前项目中遇到了同样的问题,发现目前无法在运行时访问环境变量,所以我最终得到了创建 .env 文件或本地环境变量的解决方案,正如你所说, 在构建时使用。

【讨论】:

这篇文章的作者解释说,尝试将变量传递给静态 html.js 代码是没有意义的。我终于明白了! reddit.com/r/vuejs/comments/8sbosc/…【参考方案2】:

使用您想要的配置创建一个文件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

现在您可以在&lt;Base URL&gt;/config.js 访问配置文件,您应该会看到您放入 ConfigMap 条目的确切内容。您的 HTML 文档会在加载其余的缩小的 Vue.js 代码时加载该配置映射。瞧!

【讨论】:

config.js 应该放在哪里以及在代码中使用时如何导入配置? 我只是添加了一些更详细的解释。有帮助吗? @HendrikMHalkow 你能添加一个tree 你的项目结构是什么样的吗? 嗨,如果需要在 Typescript 文件中引用 config 对象,这种方法是否有效?谢谢。【参考方案3】:

我在此处添加我的工作解决方案,供那些仍然遇到问题的人使用。我确实认为@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.[profile] 文件来设置环境变量。 例如.env.development

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

然后对于 production 我将 string constants 设置为值。 例如.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——一个小于 6MB 的 NGINX 镜像。如果您无法避免编写脚本,请在最后一条命令前添加exec,以便您的 nginx 进程替换 shell 进程。 感谢您的建议和即兴回答。我仍在使用我想出的解决方案。我对代码进行了一些小的改进。【参考方案4】:

我让它与@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

【讨论】:

【参考方案5】:

创建配置文件

在公共文件夹中:public/config.js

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

更新 index.html

更新public/index.html 以在头部末尾包含以下内容:

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

vue.config.js 不需要更新,因为我们使用 public 文件夹进行配置。

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 配置图:

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

【讨论】:

为什么不在 config.js 中定义const config = xxx: "..."?感谢您的重播。 因为配置是硬编码的,你没有在运行时修改它的选项,例如在 kubernetes 中。你必须在构建后加载它 感谢您的重播!我的问题不是很清楚。我的意思是,为什么将对象包装在一个匿名函数中并返回它,为什么不只是将一个对象分配给'config'?

以上是关于在运行时将环境变量传递给 Vue 应用程序的主要内容,如果未能解决你的问题,请参考以下文章

我可以在 Gradle 中设置一个变量并在编译时将其传递给 Java 吗?

在python运行时将数据从GUI传递给线程

在运行时将更改环境变量应用于应用程序

如何将环境变量传递给前端 Web 应用程序?

将用户定义的环境变量传递给 tomcat

如何在单个文件 vue 组件中的初始化时将道具传递给 vue 组件(vue-loader 中的依赖注入)?