你真的了解 env 吗?

1,016 阅读10分钟

环境变量

在前端工程化项目中,使用env环境变量可以在开发、测试和生产环境中使用的不同的配置项,例如接口地址、域名、API密钥等等。

通常会使用类似于webpack、vue-cli、create-react-app等工具来进行项目的构建和打包,这些工具通常都提供了一种配置环境变量的方式,可以通过 .evn 配置文件或命令行参数来指定不同环境中的配置项的值。

.env 文件在 vue-cli 、create-react-app、vite 项目中都有使用。

项目中的自动生成的环境变量

我们在使用脚手架创建项目时会自动配置和管理项目中的各种依赖项和配置项,包括环境变量。

例如我们在创建 React 应用程序时,内置了环境变量 NODE_ENVPUBLIC_URL,可以通过.env文件来配置环境变量。.env 是默认的全局配置文件,可以定义各种环境变量,并且这些环境变量会被自动加载到React应用程序中。

需要注意的是:不同框架中的环境变量需要相互区分,可以避免与其他环境变量发生冲突:

react 项目中 .evn 定义的环境变量必须以 REACT_APP_开头;

Vite 定义的环境变量以 VITE_API_ 开头;

vue-cli 中的环境变量使用 VUE_APP_进行区分。

NODE_ENV

在脚手架创建项目时内置了 NODE_ENV环境变量,主要用于判断当前项目运行的环境。

一般我们能通过用 process.env.NODE_ENV 来读取它。NODE_ENV 默认有三个可能的值,分别是:

  • development:开发环境,在运行 npm run start 命令时 NODE_ENV 的值为 development。
  • test:测试环境,运行 npm run test 则为 test。
  • production:生产环境,运行 npm run build 时将会得到 prodution。

.env 文件的配置

NODE_ENV=development   // 通过 process.env.NODE_ENV 获取用于判断当前运行环境
REACT_APP_ENV = development

PUBLIC_URL

在项目中通过PUBLIC_URL引用模块系统之外的资源路径前缀。在.env文件中设置该变量的值为一个合法的url路径,打包后,在使用该变量的文件中就会被替换成对应的值,默认情况下 PUBLIC_URL = ''。

该变量的使用方式:

  1. html 中通过 %PUBLIC_URL% 获取变量值

  1. js 中 通过 process.env.PUBLIC_URL 获取变量值
const publicUrl = process.env.PUBLIC_URL
console.log(publicUrl)

环境加载优先级

为一个特定模式准备的环境文件 (例如 .env.production) 将会比一般的环境文件 (例如 .env) 拥有更高的优先级。

在项目启动时已经存在的环境变量拥有最高优先级,并不会被 .env 文件覆写。

在执行不同环境的打包指令时 .env 文件的设置优先级

  • npm start /dev 开发环境: .env.development.local > .env.development > .env.local > .env
  • npm test 测试环境: .env.test.local > .env.test > .env (注意: test 环境没有 .env.local )
  • npm run build 生产环境: .env.production.local > .env.production > .env.local >.env

配置打包环境( vite 为例)

这里我们将以 vite 为例,配置项目运行、打包时的命令:

"scripts": {
  "dev": "yarn dev:dev",
  "dev:dev": "vite --mode dev",
  "dev:uat": "vite --mode uat",
  "dev:prod": "vite --mode prod",
  "build:dev": "vue-tsc --noEmit && vite build --mode dev",
  "build:uat": "vue-tsc --noEmit && vite build --mode uat",
  "build:prod": "vue-tsc --noEmit && vite build --mode prod",
  "preview": "vite preview"
},

Vite 打包配置配置

vite [root]  // 启动以配置文件root为入口的项目开发环境
vite serve // 启动以配置文件root为入口的项目开发环境
vite build [root] // 打包以配置文件root为入口的文件为生产环境文件
vite optimize [root] // 预构建生产环境
vite preview [root] // 构建一个本地预览静态生产环境

vite preview 不支持热更新,当程序修改不会实时更新页面,需要重新执行命令,重新构建才能获取修改后的的功能。

  • 查看 Vite 运行模式:npx vite -help。
--host [host]// 指定域名
--port <port>// 指定端口
--https // 使用 TLS+HTTP/2
--cors // 可以跨域
--open [path] // 启动自动打开服务器
--stictPort // 如果指定端口被使用退出程序
--force // 强制Vite重新执行预构建,忽视缓存
--config | -c  <file> // 指定vite的配置文件
--base path // 指定url读取文件基本路径
--clearScreen // 日志记录时,是否清屏
--logLevel | -l <level> // 指定日志等级:error|info|silent|warn
--debug | -d  <feat>  // 获取调试日志
--filter | -f <filter> // 过滤调试日志
--mode | -m <mode> // 设置env模式
--help | -h // 获取vite帮助信息
--version | -v // 获取vite版本信息
--noEmit // 运行时只进行检查,不进行编译输出,这个属性也可以在tsconfig.json文件中指定
--open // 项目启动自动打开浏览器

什么时候需要使用 --force ?

Vite缓存分为两部分:

  • 文件系统缓存:Vite会将预构建的依赖缓存到node_modules/.vite,package.json的dependencies字段和依赖lock文件,或者vite.config中相关字段配置过的,这些文件发生变化,vite就会重新构建,强制执行可以通过--force 或手动删除.vite目录。
  • 浏览器缓存:解析后的依赖请求会以http头max-age=max-age=31536000,immutable 强缓存,提高开发页面重载性能。

添加环境文件

环境文件命名如下:

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

不同环境的变量可以定义在 .env.[mode] 文件中,如 .env.dev、.env.prod 等,如果 .env 文件和 .env.[mode] 中有相同的 key,后者定义的值会覆盖前者。

这里咱们以三个环境为例编写 demo,分别是:

  • 开发环境,mode 为 dev,获取对应名为 .env.dev 的环境变量配置文件
  • 测试环境,mode 为 test,获取对应名为 .env.test 的环境变量配置文件
  • 生产环境,mode 为 prod,获取对应名为 .env.prod 的环境变量配置文件

运行时的默认环境

当执行了vite serve 或 vite dev,vite 默认运行的是开发环境(development),

当我们通过 yarn dev 启动服务时,其本质是执行 vite 的启动,开发服务器启动时会自动将 NODE_ENV 环境变量设置为 development。

这是因为在开发过程中,我们通常需要访问一些开发环境下的特殊功能和工具,而这些功能和工具通常只有在 NODE_ENV 设置为 development 时才会生效。

因此,Yarn dev 命令默认将 NODE_ENV 设置为 development,以便我们进行开发工作。

若 .env 中设置 NODE_ENV 为 production 时 vite 也会强制将其设置为 development

当执行 vite build 命令时,vite 默认环境为生产环境(production)

当你运行 build 命令时,无论你要部署到哪个环境,应该始终把 NODE_ENV 设置为 "production" 来获取可用于部署的应用程序。

执行 vite preview 命令,vite默认环境也是生产环境,因为这个命令主要是创建在本地执行的生产环境。

vite中的环境变量

Vite 是一个基于 ES Modules 的构建工具,它提供了一种新的方式来管理环境变量。在 Vite 中,我们可以通过在项目根目录下创建一个名为 .env 的文件来定义环境变量。这些环境变量可以通过 import.meta.env 对象来访问。

Vite 的环境变量实现原理是基于 Rollup 的插件机制。Vite 在内部使用了 Rollup 来构建应用程序,而 Rollup 可以通过插件来扩展其功能。Vite 借助了 Rollup 插件机制中的 load 钩子来加载环境变量。

当 Vite 构建应用程序时,它会遍历项目中的所有模块,并在加载每个模块之前调用 load 钩子。在 load 钩子中,Vite 会读取 .env 文件中定义的环境变量,并将它们添加到 import.meta.env 对象中。

这样,在应用程序中就可以通过 import.meta.env 对象来访问这些环境变量啦。

但需要注意的是:Vite 在加载环境变量时会根据当前的运行环境来选择相应的 .env 文件。

在开发环境下,Vite 会加载 .env.development 文件中定义的环境变量;

在生产环境下,Vite 会加载 .env.production 文件中定义的环境变量。

Vite 在一个特殊的 import.meta.env 对象上暴露环境变量。这里有一些在所有情况下都可以使用的内建变量:

  • import.meta.env.MODE: 应用的运行模式。当我们通过 yarn dev 启动服务时,其本质是执行 vite 启动,未显式执行 mode,所以 yarn dev 的 MODE 值为 development。
  • import.meta.env.BASE_URL: 部署应用时的基本 URL。它由 vite.config.ts 中的 base 属性指定。
  • import.meta.env.PROD: 应用是否是生产环境(即是否通过 vite build 构建)
  • import.meta.env.DEV: 应用是否运行在开发环境 是否通过vite 启动服务运行(与 PROD 属性相反)。
  • import.meta.env.SSR: 应用是否开启服端渲染模式。

vite 中获取环境变量

  1. 在 vite.config.js 中获取当前环境变量
import { loadEnv } from 'vite'
export default ({mode}) => {
  // 这里的env的值就是获取到的当前运行环境的env文件对象,env的值是一个集合 ,访问某个属性 直接env.XXX读取即可
  const env = loadEnv(mode , process.cwd())
}

需要注意的是,为了防止意外地将一些环境变量泄漏到客户端,只有以 VITE_ 为前缀的变量才会暴露给经过 vite 处理的代码。

  1. 在 vite 项目中获取当前环境变量

在生产环境中,环境变量会在构建时被静态替换,因此,在引用它们时请使用完全静态的字符串。动态的 key 将无法生效。例如,动态 key 取值 import.meta.env[key] 是无效的。

<script setup>
  
  // 直接使用import.meta.env去读取环境变量文件里面的某个属性
  
  const XXX = import.meta.env.VITE_SOME_KEY
  
</script>

vue-cli 中env的实现

Vue-cli 在内部使用了 webpack 来构建应用程序,Vue-cli 的环境变量的实现是借助了 webpack 插件机制中的 DefinePlugin 来加载环境变量。

当 Vue-cli 构建应用程序时,它会遍历项目中的所有模块,并在加载每个模块之前调用 DefinePlugin 插件。在 DefinePlugin 插件中,Vue-cli 会根据当前的运行环境来选择相应的 .env 文件,读取.env 文件中定义的环境变量,并将它们添加到 process.env 对象中。

这样,在应用程序中就可以通过 process.env 对象来访问这些环境变量了。

react 中 evn 的具体实现

同样的在 React 应用中,可以通过创建 .env 文件来定义环境变量。

这些环境变量可以通过 process.env 对象来访问。React 会自动加载 .env 文件中定义的环境变量,并将它们添加到 process.env 对象中。在 React 应用中,我们可以通过 process.env.NODE_ENV 来获取当前的运行环境。

在使用 create-react-app 创建 React 应用程序时,已经集成了 dotenv 库,可以方便地使用环境变量。在应用程序运行时,Webpack 将 .env,.env.development,.env.production 文件中定义的所有环境变量添加到 process.env 全局变量中。通过这个机制,React 应用程序可以方便地在不同的环境中配置和使用环境变量。

dotenv 的作用及实现

dotenv 是一个零依赖模块,可将 .env 文件中的环境变量加载到 process.env 中。如果需要使用变量,则配合dotenv-expand 使用。

dotenv 的实现:

  1. 读取 .env 文件
  2. 解析 .env 文件拆成键值对的对象形式
  3. 赋值到 process.env 上
  4. 最后返回解析后得到的对象
const config = function(){
  // 读取 node 执行的当前路径下的 .env 文件
  let dotenvPath = path.resolve(process.cwd(), '.env');
  // 按 utf-8 解析文件 拆分键值对,得到对象
  const parsed = parse(fs.readFileSync(dotenvPath, 'utf-8'));

  // 键值对形式赋值到 process.env 变量上,原先存在的不赋值
  Object.keys(parsed).forEach(function(key){
    if(!Object.prototype.hasOwnProperty.call(process.env, key)){
      process.env[key] = parsed[key];
    }
  });

  // 返回对象
  return parsed;
};

dotenv 库的实现原理:

用 fs.readFileSync 读取 .env 文件,并解析文件为键值对形式的对象,将最终结果对象遍历赋值到 process.env 上。

总结

总的来说,这些框架都提供了一种方便的方式来管理环境变量,并将它们添加到应用程序中。根据当前的运行环境来选择相应的 .env 文件加载环境变量,这些环境变量可以帮助我们在不同的运行环境中区分、管理和应用对应的程序配置和行为。

参考

  1. vue-cli 官网
  2. Vite 官方中文文档
  3. 前端项目集成Vite配置一览无余!