赞
踩
李俊才的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
请勿转载!
环境目录
与 .env 文件
在 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,
}
})
.env
文件vite 将会自动从 环境目录 中的下列文件加载额外的环境变量:
.env # 所有情况下都会加载
.env.local # 所有情况下都会加载,但会被 git 忽略
.env.[mode] # 只在指定模式下加载
.env.[mode].local # 只在指定模式下加载,但会被 git 忽略
例如,我在环境目录下放置了一个 .env 文件,它的内容看起来就像这个样子的:
E2E_TEST_PORT=5001
其中 =
(等号)左右边的值可以理解为键值对。
vite 中的环境加载是具有优先级的。其规则如下:
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_',
}
})
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
// ...
}
}
})
有时候我们对 vite 本身的配置需要用到一些环境变量文件中配置的某些参数值,比如我们将 vite 所使用的端口号配置在一个 .env
文件中,本小节针对的就是这样的问题。
loadEnv
APIvite 提供了一个名为 loadEnv 的编程接口用于加载 envDir
中的 .env 文件。它会加载由 prefixes
选项 所指定的环境变量(参见其类型签名)。
该 API 的类型签名为:
function loadEnv(
mode: string,
envDir: string,
prefixes: string | string[] = 'VITE_',
): Record<string, string>
举例子来说明,假设我们在 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 小节)。
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 的环境变量。在 nodeJS 中 process 对象是一个全局变量。它的 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.meta 是 ESM 中定义的一个给JavaScript模块暴露特定上下文的元数据属性的对象。它包含了这个模块的信息,比如说这个模块的URL。
如果一旦 NODE_ENV 被指定,在 vite 中 import.meta.env.MODE 与 process.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
。它与上面的方式不同在于:
[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
设置的环境变量是无法在无关的终端线程或者其它语言环境的上下文中获取的。
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. 环境变量前缀)。当然除非你定义了无前缀环境变量。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。