在现代前端工程化项目中,我们经常需要根据不同的运行环境(开发、测试、预发、生产)切换配置,比如 API 地址、密钥、调试开关等。为了避免将敏感信息或环境差异硬编码在源码中,主流脚手架都支持通过 .env 文件来管理环境变量。这套文件机制遵循 .env.[mode].[local] 的命名规则,加载时有严格的优先级和覆盖关系。下面就以 Vite (也兼容 Vue CLI、Create React App 等)为例,全面解析 .env 系列文件的用法。
一、为什么需要 .env 文件
- 环境分离 :一个变量值在开发、生产环境下不同(例如
VITE_API_BASE可能是http://localhost:3000和https://api.prod.com)。 - 安全 :敏感密钥(如第三方 AppId)不进入 Git 仓库,通过本地
.local文件或 CI 环境注入。 - 便捷:无需手动改代码,启动/构建命令配合模式自动加载对应文件。
二、文件命名规则与模式(Mode)
环境变量文件遵循以下命名模式:
text
bash
.env # 所有环境都会加载的公共变量
.env.local # 所有环境都会加载的本地私有变量(通常被 .gitignore 忽略)
.env.[mode] # 只在特定模式下加载(如 .env.development)
.env.[mode].local # 只在特定模式加载的本地私有变量
mode 到底是什么?
它由你运行项目时指定的 --mode 参数决定。例如:
vite或vite dev→ 默认 mode =developmentvite build→ 默认 mode =productionvite build --mode staging→ 自定义 mode =staging
所以你可以创建 .env.staging 来存放预发布环境的变量,构建时通过 --mode staging 切换。
三、文件加载优先级
当运行一个特定 mode 的命令时,所有匹配的文件会被按顺序加载,后加载的变量会覆盖先加载的同名变量 。以 vite build --mode staging 为例,加载顺序是:
.env(所有模式公共基础).env.local(本地覆盖,被 git 忽略).env.staging(模式特定,可提交到仓库).env.staging.local(模式特定本地覆盖,被忽略)
优先级总结:.env.[mode].local > .env.[mode] > .env.local > .env
这样设计的好处是:
团队共享的基础配置放在 .env 或 .env.staging 中并提交到 Git,开发者个人可以额外创建 .env.local 或 .env.staging.local 进行本地覆写,互不干扰。
四、变量定义规则
在 .env 文件中,所有需要暴露给前端代码的变量必须以特定前缀开头,否则会被过滤,这是为了安全防止意外泄露服务器端变量。
- Vite 项目 :前缀
VITE_ - Create React App :前缀
REACT_APP_ - Vue CLI :前缀
VUE_APP_(但也可通过配置支持其他前缀)
示例 .env.development 文件:
bash
ini
# 仅有 VITE_ 开头的变量会被注入到客户端代码中
VITE_API_BASE=http://localhost:3000/api
VITE_APP_TITLE=My Dev App
VITE_DEBUG=true
# 这个变量不会被注入,仅在 Node 端构建时可用(在 vite.config.ts 中用 process.env.DB_HOST 读取)
DB_HOST=localhost
注意:.env 文件中的值都是字符串,如果需要布尔值或数字,需要在代码中手动转换(例如 VITE_DEBUG === 'true')。
五、在代码中使用环境变量
变量注入后,你可以在 JavaScript/TypeScript 代码中通过 import.meta.env(Vite)或 process.env(CRA / Vue CLI)来访问。
Vite 项目:
ts
arduino
// .env.development 中定义了 VITE_API_BASE
const apiBase = import.meta.env.VITE_API_BASE; // "http://localhost:3000/api"
const isDebug = import.meta.env.VITE_DEBUG === 'true';
Create React App / Vue CLI:
js
ini
const apiBase = process.env.REACT_APP_API_BASE;
在 index.html 或其他模板中同样可以使用(Vite):
html
xml
<title>%VITE_APP_TITLE%</title>
TypeScript 项目中,可以为 import.meta.env 补充类型声明来获得智能提示:
ts
csharp
// env.d.ts
interface ImportMetaEnv {
readonly VITE_API_BASE: string;
readonly VITE_APP_TITLE: string;
readonly VITE_DEBUG?: string;
}
interface ImportMeta {
readonly env: ImportMetaEnv;
}
六、.local 文件与 Git 忽略策略
一个推荐的最佳实践:
- 提交 :
.env、.env.development、.env.production(仅包含无敏感的公共配置) - 忽略 :
.env.local、*.local(包含个人密钥、数据库连接等敏感信息)
在 .gitignore 中添加:
text
bash
# local env files
.env.local
.env.*.local
这样每个开发者或 CI 机器可以自由覆盖变量而不影响版本库。
七、在 vite.config.ts 中使用环境变量
有时我们需要在构建配置中使用环境变量(如根据环境切换代理目标)。Vite 配置文件可以访问所有 Node 环境变量(包括没有 VITE_ 前缀的)。但需要注意,vite.config.ts 中使用的是 process.env 或通过 Vite 的 loadEnv 工具加载。
ts
javascript
import { defineConfig, loadEnv } from 'vite';
export default defineConfig(({ mode }) => {
const env = loadEnv(mode, process.cwd(), '');
// 现在 env 包含了所有 .env 文件的变量(包括 VITE_ 前缀)
console.log(env.VITE_API_BASE);
return {
// 配置项...
};
});
loadEnv 第三个参数可以指定前缀,传 '' 表示加载所有变量(仅供 Node 端使用)。
八、常见场景示例
1. 多环境 API 地址
text
ini
.env → VITE_API_BASE=/api
.env.development → VITE_API_BASE=http://localhost:3000
.env.production → VITE_API_BASE=https://api.myapp.com
.env.staging → VITE_API_BASE=https://staging-api.myapp.com
运行 vite build --mode staging 时,最终 import.meta.env.VITE_API_BASE 为 https://staging-api.myapp.com。
2. 动态标题与特性开关
bash
ini
# .env.development
VITE_APP_TITLE=Dev App
VITE_ENABLE_ANALYTICS=false
# .env.production
VITE_APP_TITLE=My App
VITE_ENABLE_ANALYTICS=true
3. 第三方密钥本地存放
bash
ini
# .env.local (不提交)
VITE_GOOGLE_MAP_KEY=AIzaSy123456
代码读取 import.meta.env.VITE_GOOGLE_MAP_KEY 即可。
九、团队协作:如何保证拉取代码后项目可直接运行
很多开发者会担心:既然 .env.local 不提交到仓库,同事克隆代码后执行 npm run dev 缺少某些变量,项目岂不是跑不起来?
这确实是最常见的协作痛点,解决方案的核心是将变量明确区分为"公共配置"和"私密配置" ,并为团队提供清晰的指引。
1. 两类变量,两种存放策略
| 类型 | 特征 | 存放位置 | 是否提交 Git |
|---|---|---|---|
| 公共配置(非敏感) | API 地址、功能开关、环境标识、Mock 开关等 | .env.development / .env.production |
✅ 是 |
| 私密配置(敏感) | 第三方密钥、个人数据库地址、内部 Token | .env.local |
❌ 否 |
2. 提交 .env.development 和 .env.production 保障基本可运行
把项目启动所必需的、不敏感的公共配置直接写在 .env.development 中,并提交到仓库:
bash
ini
# .env.development (提交到 Git)
VITE_API_BASE = https://dev-api.mycompany.com
VITE_APP_TITLE = 我的应用(开发环境)
VITE_ENABLE_MOCK = true
任何同事拉取代码后,只需执行 npm run dev,Vite 就会自动加载 .env 和 .env.development 中的公共配置,项目便可以正常启动调试。
3. 提供 .env.example 模板,引导开发者创建本地私密配置
对于敏感变量(如个人测试密钥),绝对不能提交到仓库,但需要告知团队成员这些变量的用途和申请方式。最佳实践是在项目中提供一个不含真实值的示例文件 .env.example,并提交它:
bash
ini
# .env.example (提交,仅含占位说明)
# 复制此文件为 .env.local 并填入你的个人测试密钥
VITE_GOOGLE_MAP_KEY = your_key_here
VITE_SENTRY_DSN = your_dsn_here
新成员加入项目时的启动流程变为:
- 克隆仓库
- 执行
cp .env.example .env.local - 打开
.env.local,将占位值替换为自己的真实密钥(可从团队文档或管理员处获取) - 运行
npm run dev,项目完整可用
这样一来,即使 .env.local 不提交,团队也能非常顺畅地协作。而且因为 .env.development 已经提供了基础公共配置,哪怕暂时未配置私密变量,核心功能通常也不会完全瘫痪。
4. 线上构建的变量管理同理
线上部署(CI/CD 环境)同样不需要 .env.local 文件,而是通过平台的环境变量设置界面(如 GitHub Secrets、Vercel Env 等)注入同名的 VITE_GOOGLE_MAP_KEY。构建时 Vite 会自动读取系统环境变量并注入代码,确保线上正常运行。
十、注意事项与陷阱
- 变量替换在构建时发生 :前端代码中的环境变量会被静态替换为字符串字面量,因此不能像 Node 环境那样在运行时动态读取整个
import.meta.env,请确保在代码中直接使用import.meta.env.VITE_XXX形式。 - 前缀过滤是硬性规则 :忘记加
VITE_前缀的变量不会被注入客户端代码,只可在 Node 侧访问。 - 安全性 :所有带前缀的变量会直接打包进浏览器代码,任何人查看源码都能看见。绝对不要将服务器私钥、数据库密码等机密通过
VITE_变量暴露。 - 字符串类型:所有值都是字符串,需要布尔或数字时务必显式转换。
十一、总结
前端环境变量文件体系通过 .env.[mode].[local] 的层级设计,优雅地解决了环境配置与代码分离的需求。核心要点:
- 根据
--mode自动加载对应文件。 - 遵循严格的覆盖优先级。
- 客户端变量必须使用固定前缀(
VITE_等)才能访问。 - 利用
.local文件保护敏感本地配置。 - 团队协作时,提交
.env.development/.env.production提供公共配置,用.env.example引导开发者自行创建.env.local,保证项目开箱即用。
掌握这套机制后,你可以轻松实现多环境切换,让项目的配置管理更安全、更灵活,同时保障团队协作的顺畅性。