环境变量与模式
环境变量基础
Vite 在特殊的 import.meta.env 对象上暴露环境变量。这些常量在开发时定义为全局变量,在构建时静态替换以实现 tree-shaking。
内置常量
| 常量 | 类型 | 说明 |
|---|---|---|
import.meta.env.MODE | string | 应用运行的模式 |
import.meta.env.BASE_URL | string | 应用的基础 URL,由 base 配置决定 |
import.meta.env.PROD | boolean | 是否为生产环境 |
import.meta.env.DEV | boolean | 是否为开发环境(PROD 的相反值) |
import.meta.env.SSR | boolean | 是否在服务端渲染 |
使用示例
// 根据环境执行不同逻辑
if (import.meta.env.DEV) {
// 开发环境代码,生产构建时会被 tree-shaking 移除
console.log('开发模式')
}
if (import.meta.env.PROD) {
// 生产环境代码
console.log('生产模式')
}
// 获取基础 URL
const baseUrl = import.meta.env.BASE_URL
自定义环境变量
变量前缀
只有以 VITE_ 开头的环境变量才会暴露给客户端代码:
# .env
VITE_API_URL=https://api.example.com
DB_PASSWORD=secret # 不会暴露给客户端
// ✅ 可以访问
console.log(import.meta.env.VITE_API_URL)
// ❌ undefined
console.log(import.meta.env.DB_PASSWORD)
自定义前缀
可以通过 envPrefix 配置自定义前缀:
// vite.config.js
export default defineConfig({
envPrefix: 'APP_', // 现在以 APP_ 开头的变量会被暴露
})
# .env
APP_API_URL=https://api.example.com
.env 文件
Vite 使用 dotenv 从以下文件加载环境变量:
.env # 所有情况下都会加载
.env.local # 所有情况下都会加载,但会被 git 忽略
.env.[mode] # 只在指定模式下加载
.env.[mode].local # 只在指定模式下加载,但会被 git 忽略
加载优先级
环境变量的加载优先级(从高到低):
- 进程已有的环境变量(最高优先级)
.env.[mode].local.env.[mode].env.local.env(最低优先级)
模式特定变量
# .env.development
VITE_API_URL=http://localhost:8080
# .env.production
VITE_API_URL=https://api.example.com
变量扩展
Vite 支持在 .env 文件中使用变量扩展:
# .env
VITE_APP_NAME=MyApp
VITE_APP_TITLE=Welcome to ${VITE_APP_NAME}
注意:如果值中包含 $,需要用 \ 转义:
KEY=123
NEW_KEY1=test$foo # 结果为: test
NEW_KEY2=test\$foo # 结果为: test$foo
NEW_KEY3=test$KEY # 结果为: test123
反向顺序扩展
Vite 支持反向顺序扩展变量。例如:
VITE_FOO=foo${VITE_BAR}
VITE_BAR=bar
这会被评估为 VITE_FOO=foobar,VITE_BAR=bar。
注意:这种用法在 shell 脚本和其他工具(如 docker compose)中不起作用。Vite 支持这种行为是因为 dotenv-expand 长期以来支持这种写法,JavaScript 生态系统中的其他工具也使用支持此行为的旧版本。
为避免互操作问题,建议避免依赖这种行为。Vite 未来可能会对此行为发出警告。
Bun 用户注意事项
使用 Bun 时,请注意 Bun 会在脚本运行前自动加载 .env 文件。这种内置行为会将环境变量直接加载到 process.env 中,可能会干扰 Vite 的功能,因为 Vite 会尊重现有的 process.env 值。
模式(Mode)
默认模式
vite(开发服务器):development模式vite build:production模式
自定义模式
可以通过 --mode 选项覆盖默认模式:
# 使用 staging 模式构建
vite build --mode staging
创建对应的 .env 文件:
# .env.staging
VITE_APP_TITLE=My App (Staging)
VITE_API_URL=https://staging-api.example.com
常见模式配置
.env # 共享配置
.env.local # 本地覆盖(不提交到 git)
.env.development # 开发环境
.env.development.local # 本地开发覆盖
.env.production # 生产环境
.env.production.local # 本地生产覆盖
.env.staging # 预发布环境
.env.test # 测试环境
NODE_ENV 与 Mode 的区别
NODE_ENV 和 mode 是两个不同的概念:
| 命令 | NODE_ENV | Mode |
|---|---|---|
vite build | "production" | "production" |
vite build --mode development | "production" | "development" |
NODE_ENV=development vite build | "development" | "production" |
NODE_ENV=development vite build --mode development | "development" | "development" |
对 import.meta.env 的影响
| NODE_ENV | import.meta.env.PROD | import.meta.env.DEV |
|---|---|---|
| production | true | false |
| development | false | true |
| 其他 | false | true |
| Mode | import.meta.env.MODE |
|---|---|
| --mode production | "production" |
| --mode development | "development" |
| --mode staging | "staging" |
TypeScript 类型支持
扩展 ImportMetaEnv 接口
为获得自定义环境变量的类型提示,需要扩展 ImportMetaEnv 接口:
// vite-env.d.ts
/// <reference types="vite/client" />
interface ImportMetaEnv {
readonly VITE_APP_TITLE: string
readonly VITE_API_URL: string
readonly VITE_API_TIMEOUT: string
// 更多环境变量...
}
interface ImportMeta {
readonly env: ImportMetaEnv
}
严格类型检查
启用严格类型检查,禁止未知键:
// vite-env.d.ts
interface ViteTypeOptions {
strictImportMetaEnv: unknown
}
interface ImportMetaEnv {
readonly VITE_APP_TITLE: string
}
注意事项
不要在 vite-env.d.ts 中使用 import 语句,否则会破坏类型扩展:
// ❌ 错误
import { SomeType } from './types'
interface ImportMetaEnv {
readonly VITE_DATA: SomeType
}
// ✅ 正确
interface ImportMetaEnv {
readonly VITE_DATA: string
}
HTML 中的环境变量
Vite 支持在 HTML 文件中使用 %VAR_NAME% 语法替换环境变量:
<!doctype html>
<html>
<head>
<title>%VITE_APP_TITLE%</title>
</head>
<body>
<h1>Vite is running in %MODE%</h1>
<p>API: %VITE_API_URL%</p>
<!-- 不存在的变量会被忽略,不会替换 -->
<p>%NON_EXISTENT%</p> <!-- 保持原样 -->
</body>
</html>
HTML 变量替换的行为差异
在 JavaScript 中和 HTML 中使用环境变量有重要区别:
// JavaScript 中:不存在的变量返回 undefined
console.log(import.meta.env.NON_EXISTENT) // undefined
// HTML 中:不存在的变量保持原样
// <p>%NON_EXISTENT%</p> 输出: %NON_EXISTENT%
HTML 条件替换的局限性
由于 Vite 被许多框架使用,它故意不对复杂替换(如条件判断)持有特定观点。如果需要更复杂的 HTML 转换,可以:
- 使用现有用户插件:如
vite-plugin-html - 自定义插件:实现
transformIndexHtml钩子
// 自定义 HTML 转换插件示例
export default function htmlTransformPlugin() {
return {
name: 'html-transform',
transformIndexHtml(html, ctx) {
// 根据环境动态修改 HTML
if (ctx.bundle) {
return html.replace(
'</head>',
`<script>window.__ENV__ = ${JSON.stringify({
mode: ctx.mode,
baseUrl: ctx.baseUrl
})}</script></head>`
)
}
}
}
}
在配置中使用环境变量
加载 .env 文件
Vite 故意延迟加载 .env 文件直到用户配置解析完成后。如果需要在配置中使用环境变量,可以手动加载:
import { defineConfig, loadEnv } from 'vite'
export default defineConfig(({ mode }) => {
// 加载当前模式的环境变量
const env = loadEnv(mode, process.cwd(), '')
return {
server: {
port: env.VITE_PORT ? Number(env.VITE_PORT) : 3000,
},
define: {
__APP_VERSION__: JSON.stringify(env.VITE_APP_VERSION),
},
}
})
loadEnv 参数
loadEnv(mode, envDir, prefixes)
| 参数 | 类型 | 说明 |
|---|---|---|
mode | string | 应用运行的模式 |
envDir | string | 环境文件所在的目录 |
prefixes | string | string[] | 变量前缀,默认 "VITE_"。空字符串加载所有变量 |
最佳实践
1. 敏感信息处理
永远不要将敏感信息存储在客户端环境变量中:
# ❌ 错误 - 这些会被打包到客户端代码中,任何人都能看到
VITE_API_KEY=sk-1234567890
VITE_DB_PASSWORD=secret
# ✅ 正确 - 仅在服务端使用
API_KEY=sk-1234567890 # 不以 VITE_ 开头,不会暴露给客户端
为什么这是个问题?
环境变量的值会被直接替换到构建产物中。打开浏览器开发者工具,搜索你的 API 密钥,会发现它明文存在于 JavaScript 文件中。
对于需要保密的 API 密钥,应该:
- 使用后端服务器或 serverless 函数作为代理
- 使用环境变量但不添加 `VITE_ 前缀
- 通过代理转发请求,密钥只存在于服务器端
安全架构示例:
客户端 → 请求 → 后端 API 代理 → 使用密钥 → 第三方服务
│
└── 不包含敏感信息
2. 类型安全
始终为环境变量定义 TypeScript 类型,获得编译时检查和 IDE 智能提示:
// vite-env.d.ts
interface ImportMetaEnv {
readonly VITE_APP_TITLE: string
readonly VITE_API_URL: string
readonly VITE_API_TIMEOUT: string
readonly VITE_ENABLE_MOCK: string
}
启用严格模式(禁止未知键):
// vite-env.d.ts
interface ViteTypeOptions {
strictImportMetaEnv: unknown // 启用严格类型检查
}
interface ImportMetaEnv {
readonly VITE_APP_TITLE: string
// 如果在代码中使用了未定义的变量,TypeScript 会报错
}
3. 默认值处理
环境变量的值始终是字符串类型,需要手动转换。在代码中提供合理的默认值:
// 字符串类型
const apiUrl = import.meta.env.VITE_API_URL || 'http://localhost:8080'
// 数字类型
const timeout = Number(import.meta.env.VITE_API_TIMEOUT) || 5000
// 布尔类型
const enableMock = import.meta.env.VITE_ENABLE_MOCK === 'true'
// 可选值
const appTitle = import.meta.env.VITE_APP_TITLE ?? 'My App'
封装环境变量访问:
// config/env.js
export const config = {
apiUrl: import.meta.env.VITE_API_URL || 'http://localhost:8080',
timeout: Number(import.meta.env.VITE_API_TIMEOUT) || 5000,
enableMock: import.meta.env.VITE_ENABLE_MOCK === 'true',
isDev: import.meta.env.DEV,
isProd: import.meta.env.PROD,
}
// 使用
import { config } from './config/env'
console.log(config.apiUrl)
4. 环境变量命名
使用清晰的命名规范,让变量用途一目了然:
VITE_APP_NAME # 应用名称
VITE_APP_VERSION # 应用版本
VITE_API_URL # API 基础 URL
VITE_API_TIMEOUT # API 超时时间
VITE_ENABLE_FEATURE # 功能开关(命名以 ENABLE_ 开头)
VITE_THEME # 主题配置
命名约定建议:
- 使用大写字母和下划线
- 按功能分组(APP_、API_、ENABLE_ 等)
- 布尔开关以 ENABLE_ 或 DISABLE_ 开头
- URL 类型的变量以 _URL 结尾
5. 文档化
在项目中记录所有环境变量,方便团队成员理解和维护:
## 环境变量
| 变量名 | 说明 | 默认值 | 必需 |
|--------|------|--------|------|
| VITE_APP_TITLE | 应用标题 | - | 是 |
| VITE_API_URL | API 基础 URL | - | 是 |
| VITE_API_TIMEOUT | API 超时时间(ms) | 5000 | 否 |
| VITE_ENABLE_MOCK | 是否启用 Mock 数据 | false | 否 |
6. 环境文件管理
推荐的 .env 文件结构:
.env # 共享配置(提交到 git)
.env.local # 本地覆盖(不提交,包含开发者的个人配置)
.env.development # 开发环境特定配置
.env.development.local # 开发环境本地覆盖
.env.production # 生产环境特定配置
.env.production.local # 生产环境本地覆盖
.env.staging # 预发布环境
.env.test # 测试环境
.gitignore 配置:
# 环境变量
.env.local
.env.*.local
7. 多环境配置示例
开发环境 (.env.development):
VITE_APP_TITLE=My App (Dev)
VITE_API_URL=http://localhost:8080
VITE_ENABLE_MOCK=true
预发布环境 (.env.staging):
VITE_APP_TITLE=My App (Staging)
VITE_API_URL=https://staging-api.example.com
VITE_ENABLE_MOCK=false
生产环境 (.env.production):
VITE_APP_TITLE=My App
VITE_API_URL=https://api.example.com
VITE_ENABLE_MOCK=false
使用不同的环境构建:
{
"scripts": {
"dev": "vite",
"build": "vite build",
"build:staging": "vite build --mode staging",
"preview": "vite preview"
}
}
写在最后
环境变量让你的应用能够在不同环境中灵活运行。本章介绍了内置常量、自定义变量、.env 文件、模式系统以及 TypeScript 类型支持。
使用环境变量时请记住:敏感信息永远不要暴露给客户端,只有 VITE_ 前缀的变量会被打包。为环境变量添加类型定义可以获得更好的开发体验。