vite 中使用环境变量的相关总结

vite 相关笔记
vite 中使用 .env 的相关总结

李俊才的CSDN博客(jclee95)https://blog.csdn.net/qq_28550263?spm=1001.2101.3001.5343
邮箱 :291148484@163.com

本文地址https://blog.csdn.net/qq_28550263/article/details/129641756

请勿转载!



1. 配置选项中的 环境目录.env 文件

1.1 环境目录

vite 中, 环境目录 是一个用于存放 vite 的 .env 文件的目录,其中这些文件用于描述 vite 所使用的 环境变量

我们可以在vite配置文件中,通过 envDir 选项自定义 vite 的环境目录。例如:

// vite.config.ts
import path from 'node:path';
import { defineConfig, loadEnv } from "vite"

const ENV_DIR = path.join(__dirname, "envs");

export default defineConfig(({ command, mode }) => {
  const env = loadEnv(mode, ENV_DIR, "");
  // ... do something about env
  
  return {
    // vite configs
    // ...
    envDir: ENV_DIR,
  }
})

1.2 .env 文件

vite 将会自动从 环境目录 中的下列文件加载额外的环境变量:

.env                # 所有情况下都会加载
.env.local          # 所有情况下都会加载,但会被 git 忽略
.env.[mode]         # 只在指定模式下加载
.env.[mode].local   # 只在指定模式下加载,但会被 git 忽略

例如,我在环境目录下放置了一个 .env 文件,它的内容看起来就像这个样子的:

E2E_TEST_PORT=5001

其中 = (等号)左右边的值可以理解为键值对。

1.3 vite 中环境加载优先级

vite 中的环境加载是具有优先级的。其规则如下:

  1. Vite 执行时已经存在的环境变量有最高的优先级,不会被 .env 类文件覆盖;
  2. 一份用于指定模式的文件(例如 .env.production)会比通用形式的优先级更高(例如 .env)。

2. 环境变量前缀

2.1 包含前缀的环境变量

vite 使用 envPrefix 选项配置环境变量的前缀,该选项的值为一个 字符串 或者 一个 字符串数组
你可以参考下面配置:

// vite.config.ts
import path from 'node:path';
import { defineConfig, loadEnv } from "vite"

const ENV_DIR = path.join(__dirname, "envs");

export default defineConfig(({ command, mode }) => {
  const env = loadEnv(mode, ENV_DIR, "");
  // ... do something about env
  
  return {
    // vite configs
    // ...
    envDir: ENV_DIR,
    envPrefix: 'VITE_',
  }
})

2.2 不含前缀的环境变量

envPrefix 选项的默认值即 VITE_,如果你使用该值也可以不配置。但是 envPrefix 选项的值不能被配置为空字符串,因为这将暴露你所有的环境变量,导致敏感信息的意外泄漏。 而 vite 也注意到了这一点,因此它检测到envPrefix 选项被配置为 '' (空字符串)时 vite 将会抛出错误。但是,如果你想暴露一个不含前缀的变量,可以使用 define 选项。

在 vite 中,define 选项用于定义 全局常量替换方式。其中每项在开发环境下会被定义在全局,而在构建时被静态替换。其格式为:

define: Record<string, any>

当用 define 选项定义不含前缀的环境变量时,一个具体的例子为:

// vite.config.ts
import path from 'node:path';
import { defineConfig, loadEnv } from "vite"

const ENV_DIR = path.join(__dirname, "envs");

export default defineConfig(({ command, mode }) => {
  const env = loadEnv(mode, ENV_DIR, "");
  // ... do something about env
  
  return {
    // vite configs
    // ...
    envDir: ENV_DIR,
    envPrefix: 'VITE_',
    define: {
      'import.meta.env.ENV_VARIABLE': JSON.stringify(process.env.ENV_VARIABLE),
      // other options of define
      // ...
    }
  }
})

3. 环境变量的使用

3.1 在 vite 配置文件中直接使用

有时候我们对 vite 本身的配置需要用到一些环境变量文件中配置的某些参数值,比如我们将 vite 所使用的端口号配置在一个 .env 文件中,本小节针对的就是这样的问题。

3.1.1 loadEnv API

vite 提供了一个名为 loadEnv 的编程接口用于加载 envDir 中的 .env 文件。它会加载由 prefixes 选项 所指定的环境变量(参见其类型签名)。

该 API 的类型签名为:

function loadEnv(
  mode: string,
  envDir: string,
  prefixes: string | string[] = 'VITE_',
): Record<string, string>

3.1.2 实战例子

举例子来说明,假设我们在 env 目录下放名为 .env.env.dev.env.test.env.production 这四个.env 文件。这几个文件的内容为:
envs/.env

VITE_KEY1 = true
VITE_KEY2 = 1
VITE_KEY3 = 'string1'
VITE_KEY4 = ['1','2','3']
VITE_KEY5 = [['1','2'],['1','2']]
NOTE_FOT_VITE = "NOTE_FOT_VITE"

envs/.env.development

VITE_KEY1 = true
VITE_KEY2 = 2
VITE_KEY3 = 'string1'
VITE_KEY4 = ['2','3','4']
VITE_KEY5 = [['2','3'],['2','3']]
NOTE_FOT_VITE = "NOTE_FOT_VITE"

envs/.env.production

VITE_KEY1 = false
VITE_KEY2 = 3
VITE_KEY3 = 'string3'
VITE_KEY4 = ['3','4','5']
VITE_KEY5 = [['3','4'],['3','4']]
NOTE_FOT_VITE = "NOTE_FOT_VITE"

envs/.env.test

VITE_KEY1 = false
VITE_KEY2 = 4
VITE_KEY3 = 'string4'
VITE_KEY4 = ['4','5','6']
VITE_KEY5 = [['4','5'],['4','5']]
NOTE_FOT_VITE = "NOTE_FOT_VITE"

为了解析 .env 文件我们可以使用dotenv模块:

npm i dotenv
# or
yarn add dotenv
# or
pnpm i dotenv

然后编写一个函数用于读取所有的环境变量文件中的值,并将其添加到 process.env中。

为了不至于代码凌乱,我们新建一个文件 vite.utils.ts,位于与 vite.config.ts 相同的目录中。

// vite.utils.ts
declare type Recordable<T = any> = Record<string, T>;

// 根据你的需要,定义你的环境变量的数据类型
declare interface ViteEnv {
  VITE_KEY1: boolean;
  VITE_KEY2: number;
  VITE_KEY3: string;
  VITE_KEY4: [string][];
  VITE_KEY5: [string, string][];
}

export function wrapperEnv(envConf: Recordable): ViteEnv {
  const res: any = {};

  // 遍历所有环境名,也包括了操作系统中定义的环境遍历
  for (const envName of Object.keys(envConf)) {
    let value = envConf[envName].replace(/\\n/g, '\n');
    // 在这做一些拦截,对你的不同类型的环境变量做解析

    // 将解析 'true' 'false' 字符串转换成 boolean 类型
    if(value === 'true'){
      value=true
    }else if(value === 'false'){
      value = false
    }
    // 处理应该为数字类型的 VITE_KEY1
    else if (envName === 'VITE_KEY1') {
      value = Number(value);
    }
    // 处理可用反序列化解析为数组的 VITE_KEY4、VITE_KEY5
    else if (value && (envName === 'VITE_KEY4' || envName === 'VITE_KEY5')) {
      try {
        value = JSON.parse(value.replace(/'/g, '"'));
      } catch (error) {
        value = '';
      }
    }
    // 记录到返回值中
    res[envName] = value;
    
    // 绑定到 process.env 上,成为 process.env.xxx (xxx表示envName)
    if (typeof value === 'string') {
      process.env[envName] = value;
    } else if (typeof value === 'object') {
      process.env[envName] = JSON.stringify(value);
    }
  }
  return res;
}

现在我们编写一个用于测试的 vite.config.ts

import path from 'node:path';
import { defineConfig, loadEnv } from "vite"
import { wrapperEnv } from './vite.utils'

const ENV_DIR = path.join(__dirname, "envs");

export default defineConfig(({ command, mode }) => {
  console.log('mode =',mode);
  
  const env = loadEnv(mode, ENV_DIR, "");
  const viteEnv = wrapperEnv(env);
  const { VITE_KEY1, VITE_KEY2, VITE_KEY3, VITE_KEY4, VITE_KEY5 } = viteEnv;
  
  console.log('VITE_KEY1 =',VITE_KEY1);
  console.log('VITE_KEY2 =',VITE_KEY2);
  console.log('VITE_KEY3 =',VITE_KEY3);
  console.log('VITE_KEY4 =',VITE_KEY4);
  console.log('VITE_KEY5 =',VITE_KEY5);
  

  return {
    
    plugins: [],
    envDir: ENV_DIR,
    envPrefix: 'VITE_',
    define: {
      'import.meta.env.ENV_VARIABLE': JSON.stringify(process.env.ENV_VARIABLE),
      // other options of define
      // ...
    }
  }
})

在存放配置文件的目录下运行 vite,可以看到输出效果:

在这里插入图片描述

可以看出,读取的是 envs/.env.development 文件中的配置,并且都正确转换成了我们需要的类型。由于 mode 的值为 development,因此 vite 的 loadEnv 函数读取的是 envDir 下面的 .env.development 文件中的配置,因为这就匹配了 .env.[mode] 的模式(见1.2 小节)。

3.1.3 使用 cross-env 控制 mode

3.1.2 小节 的例子中,你如果细心一点,一定会提出一个问题。那就是 vite 提供的 defineConfig 函数中的 mode 的值是从哪里来的。首先,一个可以用但是不好用的方法是使用 vite 的配置项指定:

// ...

export default defineConfig(({ command, mode }) => {
  //...
  return {
    // ...
    mode: 'development'
  }
})

vite 内部会自动地把你指定的 mode 值绑定到 import.meta.env.MODE 上,作为 vite 所使用的 运行模式 值,单你不作任何指定的时候,这个值就是 development(默认值),而闯入defineConfig 的回调函数的选项参数中的 mode 在 vite 内部所使用的值就是 import.meta.env.MODE 上所绑定的值。

这是非常麻烦的!

因为在实际开发中,我们会在项目的 package.json 文件中指定不同的 script,用于 开发测试构建,等等不同的环境。如果使用上面这种方式,这意味着我们每次运行不同环境的命令项需要手动地在 vite.config.ts 中去更改我们的mode配置项的值。

那有什么办法解决这个问题呢?

我们已经说过, vite 使用内建变量 import.meta.env.MODE 作为应用运行的模式。这也就是说我们只要在 package.json 文件中的 script 的不同项运行前预先 将这个模式值更改好就不在需要每次运行前都配置 mode 选项了。

实际上 nodeJS 中有一个名为环境变量 process.env.NODE_ENV 的环境变量。在 nodeJSprocess 对象是一个全局变量。它的 env 属性可以直接从操作系统中获取。

比如在 Windows 系统中定义环境变量:
在这里插入图片描述
你是可以在本机上的任何 nodejs 脚本中获取到它的值的:

console.log(process.env.NODE_ENV);  // development

甚至这还会影响你的依赖安装——如果系统环境变量的NODE_ENV值设置为production(process.env 将读取系统中该变量值作为自己的值),那么你使用诸如下面的命令:

npm install
# or
yarn
# or
pnpm install

可能导致各种错误。因为——这些命令在 production 模式下只会安装 package.json文件中 dependencies 字段下所指定的依赖,而哪些作为开发环境下的依赖(一般使用-D选项添加)的由 devDependencies 字段所指定的依赖将全部被忽略!

import.metaESM 中定义的一个给JavaScript模块暴露特定上下文的元数据属性的对象。它包含了这个模块的信息,比如说这个模块的URL。

如果一旦 NODE_ENV 被指定,在 vite 中 import.meta.env.MODEprocess.env.NODE_ENV 一致。

因此你可以通过设置这些变量值来完成对 vite 的 mode 的配置。不过Linux 系统 和 Windows 系统上设置还有一些不一样,windows 可以通过图形化界面在 高级系统设置 中添加环境变量——这也是手动的,更加麻烦。而 Linux 上可以使用 set 命令进行指定。

有一种方案是使用 powershell,它是跨平台的,不论你是 Linux、Windows 还是 macOS,都可以使用 powerhsell 调用 .net 完成。因此你可以这样配置你的 script

{
  // ...
  script: {
    "get:env": "pwsh -Command \"[System.Environment]::GetEnvironmentVariable('NODE_ENV')\"",
    "set:development": "pwsh -Command \"[System.Environment]::SetEnvironmentVariable('NODE_ENV','development','User')\"",
    "set:production": "pwsh -Command \"[System.Environment]::SetEnvironmentVariable('NODE_ENV','production','User')\"",
  }
}

其中:

  • get:env 命令用于从系统中读取当前 NODE_ENV 的值;
  • set:development 命令用于将 NODE_ENV 的值设置为 development
  • set:production 命令用于将 NODE_ENV 的值设置为 production

这样你只需要将需要改变环境的命令添加到相应的script前运行就可以改变 NODE_ENV 环境变量值了。

例如:

{
  // ...
  script: {
    "set:development": "pwsh -Command \"[System.Environment]::SetEnvironmentVariable('NODE_ENV','development','User')\"",
    "set:production": "pwsh -Command \"[System.Environment]::SetEnvironmentVariable('NODE_ENV','production','User')\"",
    "build": "pnpm run set:production && vite build",
    "dev": "pnpm run set:production && vite"
  }
}

不过这还是会由一点问题,在 Windows 系统上打开一个终端,使用这种方式设置了新的环境变量值,即使你改变了环境变量的值,程序也不会获取环境变量最新的值,而是终端窗口第一次获取的环境变量值。这是为了运行效率提升还是什么其它原因为可得知。因此你不得不每次在运行 'set:xxx' 的命令后打开一个新的终端窗口才会生效。因此看起来也有一些麻烦。

不过好在有一个模块帮我们解决了这样的问题,它就是 cross-env。它与上面的方式不同在于:

  • 上面的 poweshell 命令 [System.Environment]::SetEnvironmentVariable('NODE_ENV','xxx','User') 真实地在操作系统的当前用户下新建了一个永久性的环境变量 NODE_ENV(若不存在),并将其修改为'xxx'。这种设置下,即使你的操作系统重启过后仍然可以读取到,并且会影响到你的系统上所有 nodeJS 有关程序的运行——因为它是公用的;
  • cross-env并没有对新建的环境变量做永久性的保存,它只是你当前的运行上下文中设置了值,并且没有写入到系统中进行永久性的保存。

若要使用 cross-env ,你需要先使用下面的方式进行安装:

npm i cross-env -D
# or
yarn add -D cross-env
# or
pnpm i cross-env -D

与上面的配置类似,我们也是在 script 需要指定环境变量的脚本运行前指定,例如:

{
  // ...
  script: {
    // ...
    "build": "cross-env NODE_ENV=production vite build && node postBuild.js"
  }
}

注意:
使用 cross-env 设置的环境变量是无法在无关的终端线程或者其它语言环境的上下文中获取的。

3.2 在 客户端源码 中使用

vite 所加载的环境变量也会通过 import.meta.env 以字符串形式暴露给客户端源码。这意味着你可以从 import.meta.env 上获取。
例如在你的 .env 定义了一些环境变量:

VITE_SOME_KEY=123
DB_PASSWORD=foobar

在你的客户端代码中,可以这样使用

console.log(import.meta.env.VITE_SOME_KEY) // 123
console.log(import.meta.env.DB_PASSWORD) // undefined

第二个为 undefined 的原因是没有VITE_前缀(请参考2. 环境变量前缀)。当然除非你定义了无前缀环境变量。

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

jcLee95

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值