.env 文件以简单的键值对格式存储环境变量,并将配置与应用程序的源代码分离。根据 The Twelve-Factor App(2011),配置必须严格与代码分离,而 .env 文件已成为这种方法的标配。.env File 允许在不重新编译项目的情况下替换不同的 API 密钥、服务器 URL 和编译标志的值。
要点
.env File 是一个配置文件,以简单的文本格式 KEY=VALUE 存储环境变量。每行包含一个变量:键名称和其值,用等号分隔。
.env 文件解决了现代开发的一个基本问题:不同的环境(本地、测试、生产)需要完全不同的设置。本地机器上的 API 服务器 URL 是 http://localhost:8080,在生产服务器上是 https://api.production.com。如果这些值直接硬编码在应用程序代码中,每次为不同环境构建都需要修改源代码。
将配置存储在应用程序主代码之外的做法在 The Twelve-Factor App(2011)宣言中得到了标准化,该宣言将环境变量指定为配置应用程序的唯一正确方式。根据 JetBrains Developer Ecosystem(2024)的调查,超过 67% 的移动开发者在他们的项目中使用 .env 文件。
对于移动开发,.env 提供了额外的好处:值在构建阶段通过 Gradle(Android)或 xcconfig(iOS)替换,允许为开发、预发布和生产创建单独的构建,而无需修改源代码。
.env 在团队工作中特别有用:每个开发者创建自己的本地 .env,包含针对其环境的设置(本地数据库路径、调试 API 密钥),而通用设置则保存在存储库中的 .env.example 中。这消除了 git pull 后因缺少开发者不知道的环境变量而导致构建失败的情况。新团队成员只需将 .env.example 复制到 .env 并填写自己的本地值。
.env 格式极其简单:每行是一个 KEY=VALUE 形式的变量。等号周围的空格通常被忽略,但在大多数库中被视为值的一部分,因此最好避免使用。
注释以 # 符号开头——其后整行被忽略。空行也会被跳过。如果值包含空格,则用双引号或单引号括起来。
# 主要环境设置
APP_NAME=MyMobileApp
APP_ENV=development
# API 配置
API_BASE_URL=http://localhost:3000/api
API_TIMEOUT=30000
# 敏感数据
DB_PASSWORD=secret_password_123
JWT_SECRET=your_jwt_secret_key
.env 中的所有变量都是字符串,但加载库可以将它们转换为所需类型。转义特殊字符使用反斜杠和引号。如果值包含 # 符号作为文本的一部分,需要将其转义为 \#。
KEY=value 或 KEY="value with spaces"PORT=8080DEBUG=trueKEY=line1\
line2DB_URL=${DB_HOST}:${DB_PORT}加载 .env 时,库可以执行变量插值——将一个键的值替换到另一个键中。例如,变量 DATABASE_URL=postgres://${DB_USER}:${DB_PASS}@localhost/db 将展开同一个文件中的 DB_USER 和 DB_PASS。
.env 的连接方式取决于平台。Android 使用 Gradle 插件,iOS 使用 xcconfig 配置文件,而像 Flutter 这样的跨平台解决方案则使用专门的库。
在 Android 中,.env 通过 gradle-dotenv 插件加载。插件从项目根目录读取 .env 并将值添加到 BuildConfig,之后在 Kotlin 或 Java 代码中通过生成的字段可用。
// build.gradle.kts(应用级别)
plugins {
id("co.uzzu.dotenv") version "4.0.0"
}
android {
buildFeatures {
buildConfig = true
}
}
kotlin {
// 代码中的访问:BuildConfig.API_BASE_URL
buildConfigField("String", "API_BASE_URL",
"\"" + dotenv.get("API_BASE_URL") + "\"")
}
在 iOS 中,环境变量通常通过 xcconfig 文件配置。要在 Swift 中加载 .env,使用 DotEnv 库或带有自定义键的内置 Info.plist 机制。
// 在 Swift 项目中加载 .env
import DotEnv
struct AppConfig {
static func load() {
let env = DotEnv(Bundle.main)
env.load()
let apiURL = ProcessInfo.processInfo
.environment["API_BASE_URL"] ??
"https://default.api.com"
}
}
Flutter 有 flutter_dotenv 包,它在应用程序初始化时从 .env 加载变量。.env 文件放置在项目根目录,变量通过 dotenv 类可用。
// pubspec.yaml
dependencies:
flutter_dotenv: ^5.1
// main.dart — 启动时加载
import 'package:flutter_dotenv/flutter_dotenv.dart';
void main() async {
await dotenv.load(fileName: '.env');
var apiUrl = dotenv.get('API_BASE_URL');
runApp(MyApp(baseUrl: apiUrl));
}
所有三种方法都有一个共同原则:.env 在构建阶段或应用程序启动时加载,值被缓存并通过生成的常量在代码中使用。这防止了敏感数据进入存储库。
对于 React Native,使用 react-native-config 包,它在构建阶段自动从项目根目录的一个 .env 文件生成 Android 的 BuildConfig 类和 iOS 的 Info.plist 常量。这对于使用 Expo 或 bare workflow 的初创公司尤其方便:根级别的一个 .env 文件就足够了,所有平台都能获得相同的环境变量,无需重复配置。
尽管有诸多优点,.env 并非在生产环境中存储机密的完整解决方案。它提供基本级别的保护,但如果使用不当可能导致机密数据泄露。
最重要的规则——.env 绝不能进入版本控制系统。文件创建后立即添加到 .gitignore,存储库中只提交带有空或虚构值的示例文件 .env.example。
# .env.example — 提交到存储库
APP_NAME=
APP_ENV=development
API_BASE_URL=http://localhost:3000
API_TIMEOUT=30000
# DB_PASSWORD — 即使在示例中也不要填写!
# JWT_SECRET — 即使在示例中也不要填写!
# .gitignore
# Dotenv 文件
.env
.env*.local
对于生产项目,建议使用专业的机密管理解决方案。.env 在生产环境中仅当文件位于服务器的 document-root 之外并具有严格的访问权限时才允许使用。
根据 Snyk State of Open Source Security(2024),通过存储库泄露 .env 文件是受访公司中超过 12% 的 API 密钥泄露事件的原因。使用单独的机密管理器可以将此风险降至零。
通过实施pre-commit 钩子(使用 husky 和 lint-staged 等工具)可实现额外保护,这些工具检查开发者是否意外将 .env 添加到提交中。像 git-secrets(AWS)和 talisman 这样的工具会扫描每个提交以查找 API 密钥、令牌和密码模式,并在检测到时阻止提交。对于 CI 管道,建议添加 detect-secrets 检查——一个自动扫描器,即使在开发者出错时也不会让 .env 文件进入存储库。
常见问题
不需要,.env 不应提交到 Git。该文件包含敏感数据,应添加到 .gitignore。取而代之的是,将带有所有必需变量模板的 .env.example 放置在存储库中。
.env — 包含生产值的实际文件,永远不会被提交。.env.example 文件包含相同的键,但带有空或虚构的值——它作为新开发者的示例提交到存储库。
可以,但没有额外保护则不推荐。如果在生产服务器上使用 .env,则该文件必须位于 Web 服务器的 document-root 之外,具有 600 访问权限(仅所有者)。对于关键项目,更推荐使用机密管理器。
通过 gradle-dotenv 插件(co.uzzu.dotenv)。插件从项目根目录读取 .env 并将值导出到 BuildConfig。变量在编译阶段以 BuildConfig.VARIABLE_NAME 的形式在代码中可用。
是的,许多解析器支持 ${VAR_NAME} 格式的插值。例如,URL=${HOST}:${PORT} 将从同一文件中替换 HOST 和 PORT 的值。但此功能取决于具体的加载库。
总结
我们将开发一款交钥匙移动应用程序
IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。