浅析环境变量

为什么需要环境变量

现代前端项目往往有很多环境,比如开发环境、联调环境、测试环境、灰度环境、线上环境,不同环境请求的接口服务是不同的,如果人工维护请求服务的地址,一旦虚心大意忘记切换服务地址,很有可能导致线上出现事故。

能否有一个自动的环境管理方案保证接入的服务无误呢

environment variables使用方式

命令行中手动指定环境变量

pnpm run dev NODE_ENV=development

.env文件统一配置

1
2
3
4
.env                # loaded in all cases
.env.local # loaded in all cases, ignored by git
.env.[mode] # only loaded in specified mode
.env.[mode].local # only loaded in specified mode, ignored by git
1
2
3
4
"scripts": {
"dev": "vite --mode=development",
"test": "vite --mode=test",
},

envs

常见项目中的environment variables

基于vite构建

存: .env文件中VITE_变量名=xxx

取:import.meta.env.VITE_变量名

注意,不以VITE_开始的自定义变量不会被存入环境变量,这是为了避免滥用和误用

利用create-react-app或vue-cli等基于webpack的脚手架构建

存: *.env文件中REACT_APP_变量名=xxx

取:process.env.REACT_APP_变量名

注意,除了NODE_ENV,不以REACT_APP_VUE_APP_开始的变量不会被存入环境变量,这是为了避免滥用和误用

基于webpack构建

webpack打包后的结果运行在浏览器上,浏览器上并没有process.env对象,所以需要通过definePlugins自己定义到全局环境

注意:

  1. webpack的mode并不是指定环境变量,而是指定打包模式
  2. webpack运行在node环境,打包结果运行在浏览器上,浏览器环境并没有process.env,需要在webpack中处理,使得浏览器可以访问环境变量
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
// webpack.config.js

const { DefinePlugin } = require('webpack');

// 需要用cross-env+dotenv配置环境变量
require('dotenv').config();

const envObj = {};
// 对于cra,仅能以REACT_APP_开头
Object.keys(process.env).forEach((key) => {
if (key.startsWith('REACT_APP_')) {
envObj[key] = process.env[key];
}
});

console.log(process.env);

// 对于vue-cli,仅能以VUE_APP_开头
// Object.keys(process.env).forEach((key) => {
// if (key.startsWith('VUE_APP_')) {
// envObj[key] = process.env[key];
// }
// });

module.exports = {
mode: 'development', // 指定打包模式而不是环境变量
entry: {
app: './app.js',
},
output: {
filename: './bundle.js',
},
plugins: [
new DefinePlugin({
'process.env': envObj,
}),
],
};

深入解析

process.env到底是什么?

process.env 是 Node.js 中的一个全局对象,用于访问当前进程的环境变量。环境变量是在操作系统或运行 Node.js 进程的环境中定义的键值对。

管理process.env——cross-env

无论是webpack还是vite都是运行在nodejs环境上,而node环境下process.env原本是没有NODE_ENV属性的,所以需要通过命令行修改环境变量

不同操作系统的node命令差别比较大,比如想要添加一个NODE_ENV变量:

  • Windows写法:"dev": "set NODE_ENV=test && node test.js"
  • Mac写法:"dev": "export NODE_ENV=test && node test.js"

为了解决在不同操作系统上设置环境变量的差异性,引入第三方库cross-env,它是一个在跨平台环境下设置环境变量的命令行工具和nodejs包,提供了一个统一的方式来设置环境变量,无论在哪个操作系统上运行,都可以正常工作

"dev": "cross-env NODE_ENV=test node test.js"

读取.env文件并注入process.env——dotenv

node不会自动读取.env文件,可以借助第三方库dotenv,自动解析根目录下所有.env的文件

1
2
require('dotenv').config();
console.log(process.env.NODE_ENV);

import.meta.env VS process.env

process.env是一个node环境中的全局变量,浏览器端并不能访问

因此虽然运行在node环境下的打包工具可以访问,但是运行在浏览器上的打包结果并不能访问process.env

对于这个问题,webpack给出了解决方案,简单来说就是通过definePlugins将process.env定义到全局

随着ESM的发展,tc39终于在ES2020中新增了import.meta作为模块的元属性,可以在支持ESM的浏览器中获取环境变量,但不能在旧版本浏览器和node环境下使用,vite目前已经支持这种更原生的方式

流程总结

  1. 通过命令行(一般用cross-env保证跨操作系统的兼容性)赋予代码运行时的NODE_ENV变量
  2. 通过dovenv读取当前环境下的.env文件
  3. 把dotenv读取到的数据注入到代码全局环境

实际应用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// src/config/index.ts
const env = import.meta.env;

interface EnvConfig {
baseURL: string;
}

const config: Record<string, EnvConfig> = {
development: {
baseURL: 'http://localhost:5001',
},
production: {
baseURL: 'http://localhost:5001',
},
};

export default {
baseURL: config[env.MODE].baseURL,
};

后续发送请求的时候可以在axios里面设置request interceptor,将config.baseURL作为基本路径,后续再拼接陆路由


浅析环境变量
https://hugtyftg.github.io/2024/05/26/浅析环境变量/
作者
mmy@hugtyftg
发布于
2024年5月26日
许可协议