在实际的项目开发流程中,一套代码通常需要部署到多个环境:开发环境(dev)供本地调试,测试环境(test)供 QA 验证,预发布环境(staging)用于上线前的最后确认,生产环境(prod)面向真实用户。不同环境有着不同的 API 地址、资源路径、调试开关、第三方密钥等参数,如果依靠手动修改代码来切换,极易造成配置错乱甚至线上事故。因此,通过工程化手段实现环境隔离是前端工程化的基本要求。
17.4.1 环境变量的核心原理
前端构建工具(Vite / Webpack)在打包时会通过环境变量向代码注入配置。这些变量在构建时被静态替换,因此可以在不修改源码的情况下,让同一个项目针对不同环境生成不同配置的产物。
典型的环境差异配置包括:
- API 基础地址(BASE_URL)
- 静态资源公共路径(PUBLIC_PATH)
- 调试模式开关(DEBUG)
- 第三方服务密钥(地图 SDK、埋点 ID 等,敏感密钥绝不可出现在前端环境变量中)
- 构建产物版本号、构建时间等
17.4.2 Vite 中的多环境配置
Vite 原生支持 .env 文件来管理环境变量,配合不同的模式(mode)自动加载对应的环境文件。
约定文件命名:
.env # 所有环境共享的变量
.env.development # 开发环境(vite 默认 mode 为 development)
.env.test # 自定义测试环境
.env.staging # 自定义预发布环境
.env.production # 生产环境(vite build 默认 mode 为 production)
加载优先级:
当运行 vite --mode staging 时,加载顺序为:
.env(通用).env.staging(覆盖通用变量).env.staging.local(本地覆盖,通常加入.gitignore)
后加载的文件中的变量会覆盖先加载的同名变量。
示例 .env.development:
# 开发环境
VITE_API_BASE_URL=http://localhost:3000/api
VITE_APP_TITLE=MyApp(DEV)
VITE_DEBUG=true
示例 .env.production:
# 生产环境
VITE_API_BASE_URL=https://api.example.com
VITE_APP_TITLE=MyApp
VITE_DEBUG=false
注意:Vite 只暴露以 VITE_ 开头的变量给客户端代码,这是为了防止意外将敏感信息(如数据库密码)泄露到前端。在代码中通过 import.meta.env 访问:
const apiBase = import.meta.env.VITE_API_BASE_URL;
const debug = import.meta.env.VITE_DEBUG === 'true'; // 所有变量值均为字符串
TypeScript 类型提示:
在 src/vite-env.d.ts 中扩展 ImportMetaEnv 接口:
/// <reference types="vite/client" />
interface ImportMetaEnv {
readonly VITE_API_BASE_URL: string;
readonly VITE_APP_TITLE: string;
readonly VITE_DEBUG: string;
}
interface ImportMeta {
readonly env: ImportMetaEnv;
}
17.4.3 自定义模式与构建命令
通过 --mode 参数可以指定任意环境模式,Vite 会自动加载对应的 .env.[mode] 文件:
// package.json 中的脚本
{
"scripts": {
"dev": "vite",
"build:test": "vite build --mode test",
"build:staging": "vite build --mode staging",
"build:prod": "vite build --mode production"
}
}
这样,CI/CD 流水线只需执行对应的构建命令即可生成不同环境的包。
17.4.4 Webpack 中的多环境配置(补充参考)
在 Webpack 项目中,通常使用 dotenv 或 webpack.DefinePlugin 来注入变量。如果你还在维护基于 CRA 的项目,其环境变量机制与 Vite 类似,但只暴露以 REACT_APP_ 开头的变量。
使用 webpack.DefinePlugin 可以在编译时将变量替换为字面量:
const webpack = require('webpack');
const dotenv = require('dotenv');
// 加载对应环境文件
const envConfig = dotenv.config({ path: `.env.${process.env.NODE_ENV}` }).parsed;
module.exports = {
plugins: [
new webpack.DefinePlugin({
'process.env.API_BASE_URL': JSON.stringify(envConfig.API_BASE_URL)
})
]
};
17.4.5 安全原则与最佳实践
- 敏感密钥绝不可进入前端代码
所有环境变量在构建时会被写入源码包,任何前端变量都是公开的。数据库密码、服务端 API 密钥等敏感信息必须留在服务端,前端仅保存服务端对外提供的接口地址。
- 将 .env 文件加入 .gitignore
尤其 .env.local 和 .env.*.local 可能包含个人开发配置,不应提交到版本库。团队共享的变量放在 .env、.env.development 等文件中并提交,但务必确认其中没有密钥。
- 使用 CI/CD 注入构建变量
对于生产环境等敏感配置,在 CI 环境变量面板中设置,运行时注入而非写在代码仓库中。例如,Docker 构建或 GitHub Actions 中可以安全地传递 VITE_API_BASE_URL。
- 运行时配置的灵活方案
如果希望同一构建包在不同环境生效(不重新构建),可以在公共目录放置一个静态 config.js 文件,在 index.html 中通过 <script> 引入,并将运行时配置挂载到 window 对象上。不过这种方式破坏了构建产物的纯静态性,更适合 ToB 私有部署场景。
- 验证环境变量
在应用入口处提前校验必需的环境变量,避免运行到一半才报错:
if (!import.meta.env.VITE_API_BASE_URL) {
throw new Error('缺少环境变量 VITE_API_BASE_URL');
}
17.4.6 总结
多环境配置是前端工程化的基础能力。Vite 通过简洁的 .env 文件机制和模式选择,让环境隔离变得轻而易举。核心要点是:
- 不同环境的配置写成独立文件,版本控制提交通用部分,敏感信息走 CI 注入。
- 只暴露必要的变量给前端,严格遵循安全边界。
- 利用构建命令的
--mode参数在流水线中生成不同环境的部署包。
这套流程确保代码在不断流转的开发、测试、预发布直至上线的过程中,配置准确、安全、可追溯,是保障多环境协作井然有序的关键。