跳到主要内容

环境变量与模式

环境变量基础

Vite 在特殊的 import.meta.env 对象上暴露环境变量。这些常量在开发时定义为全局变量,在构建时静态替换以实现 tree-shaking。

内置常量

常量类型说明
import.meta.env.MODEstring应用运行的模式
import.meta.env.BASE_URLstring应用的基础 URL,由 base 配置决定
import.meta.env.PRODboolean是否为生产环境
import.meta.env.DEVboolean是否为开发环境(PROD 的相反值)
import.meta.env.SSRboolean是否在服务端渲染

使用示例

// 根据环境执行不同逻辑
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 忽略

加载优先级

环境变量的加载优先级(从高到低):

  1. 进程已有的环境变量(最高优先级)
  2. .env.[mode].local
  3. .env.[mode]
  4. .env.local
  5. .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=foobarVITE_BAR=bar

注意:这种用法在 shell 脚本和其他工具(如 docker compose)中不起作用。Vite 支持这种行为是因为 dotenv-expand 长期以来支持这种写法,JavaScript 生态系统中的其他工具也使用支持此行为的旧版本。

为避免互操作问题,建议避免依赖这种行为。Vite 未来可能会对此行为发出警告。

Bun 用户注意事项

使用 Bun 时,请注意 Bun 会在脚本运行前自动加载 .env 文件。这种内置行为会将环境变量直接加载到 process.env 中,可能会干扰 Vite 的功能,因为 Vite 会尊重现有的 process.env 值。

模式(Mode)

默认模式

  • vite(开发服务器):development 模式
  • vite buildproduction 模式

自定义模式

可以通过 --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_ENVmode 是两个不同的概念:

命令NODE_ENVMode
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_ENVimport.meta.env.PRODimport.meta.env.DEV
productiontruefalse
developmentfalsetrue
其他falsetrue
Modeimport.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 转换,可以:

  1. 使用现有用户插件:如 vite-plugin-html
  2. 自定义插件:实现 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)
参数类型说明
modestring应用运行的模式
envDirstring环境文件所在的目录
prefixesstring | 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_ 前缀的变量会被打包。为环境变量添加类型定义可以获得更好的开发体验。