Docker打包如何注入vite环境变量

830 阅读3分钟

原文地址

遇到的问题

最近优化修改了下 resume-online 这个小项目, 是通过docker-compose 一键部署的。 遇到个问题,想来有些朋友肯定遇到过。 写出来分享下。

我们知道, 在vue3 中,vite 允许我们在 .env 文件中定义以 VITE_ 开头的环境变量, 然后在运行时环境下,我们就可以通过 import.meta.env.VITE_XXX 去访问使用这个变量。 vite 在我们运行 build 打包命令的时候也会帮我们处理环境变量文件,将他们转为静态值插入到我们的代码中。这么做的目的是我们可以在运行时也可以访问到环境变量了。

我想要将一些环境变量在打包之前定义在 docker-compose.yml 文件中,这样以来,我们想要修改这些变量的时候,就会很方便。 于是我是这么做的。

  1. 在 docker-compose.yml 文件中通过 args 选项注入环境变量
#......
services:
  resume-web:
    container_name: 'resume-web'
    restart: always
    build:
      context: ./web/
      dockerfile: Dockerfile
      args:
        - VITE_ADMIN_PASSWD=1234  # 构建阶段注入环境变
    ports:
      - 83:80
    depends_on:
      - resume-server
    networks:
      - online-resume
#......      
  1. 在 Dockerfile 文件中接收并定义 node 的环境变量
# 设置环境变量
ARG VITE_ADMIN_PASSWD
ENV VITE_ADMIN_PASSWD=$VITE_ADMIN_PASSWD

这时候会发现我们在运行时是拿不到 import.meta.env.VITE_ADMIN_PASSWD 这个变量的。

具体问题

为什么我们拿不到这个变量呢? 其实是因为我们在 Dockerfile 中写入的环境变量是 node 的环境变量。 我们可以通过 process.env.VITE_ADMIN_PASSWD 访问到。不过我们在运行时是访问不到这个环境变量的。

那么问题其实在于,用什么方式将这个node环境变量,传给 Vite 处理,然后我们可以通过 import.meta.env.VITE_ADMIN_PASSWD 这种方式在运行时访问到。

Vite 的 define 配置项

Vite 提供了一个全局变量定义的配置项 —— #define. 它使用 esbuild define 来进行替换, 它同样在build的时候会被静态的替换。

文档中也给出了示例:

export default defineConfig({
  define: {
    __APP_VERSION__: JSON.stringify('v1.0.0'),
    __API_URL__: 'window.__backend_api_url',
  },
})

我们借由这个能力,实际可以如下定义:

......
export default defineConfig(({ command, mode }) => ({
  base: './',
  plugins: [vue(), vueJsx()],
  define: command === 'build' ? {
    'import.meta.env.VITE_ADMIN_PASSWD': process.env.VITE_ADMIN_PASSWD,
    'import.meta.env.VITE_PASSWD': process.env.VITE_PASSWD,
    'import.meta.env.VITE_PASSINPUTPAGE_BG': process.env.VITE_PASSINPUTPAGE_BG,
    'import.meta.env.VITE_PASSWD_INPUT_LABEL': process.env.VITE_PASSWD_INPUT_LABEL,
    'import.meta.env.VITE_SUCCESS_TITLE': process.env.VITE_SUCCESS_TITLE,
    'import.meta.env.VITE_SUCCESS_CONTENT': process.env.VITE_SUCCESS_CONTENT,
    'import.meta.env.VITE_ERROR_TITLE': process.env.VITE_ERROR_TITLE,
    'import.meta.env.VITE_ERROR_CONTENT': process.env.VITE_ERROR_CONTENT,
  } : {},
  resolve: {
  ......

这样,就可以将node环境变量传递给 Vite 了, 它将定义全局的常量。 然后我们就可以在运行时中正常使用了。

不过有一点需要注意,在运行时中访问这些环境变量的时候,应该使用解构。 否则可能会将字符串解析为变量。

例如:

// 假设 VITE_ADMIN_PASSWD 的值为 jayce
if(!import.meta.env.VITE_ADMIN_PASSWD) {
......
}
//这段代码会被解析为:
if(!jayce) {// jayce 并不是变量
......
}

应该这样去使用:

 const { VITE_ADMIN_PASSWD } = import.meta.env
  if (!VITE_ADMIN_PASSWD) {
}

这种方案算是比较能方便的解决问题,但是显然它并不是最佳方案。还有一些其他的方案,思路都是差不多的,例如可以在 Vite 进行打包之前,通过shell 脚本将我们传递的值添加到 .env 文件。

我这里是觉得这样更简单一些。