Super Productivity 环境配置完全指南:.env 动态变量与 TypeScript 静态环境的混合架构
【免费下载链接】super-productivitySuper Productivity is an advanced todo list app with integrated Timeboxing and time tracking capabilities. It also comes with integrations for Jira, GitLab, GitHub and Open Project.项目地址: https://gitcode.com/GitHub_Trending/su/super-productivity
本篇技术指南围绕 Super Productivity 仓库中 docs/ENV_SETUP.md 文档展开,系统讲解该项目"静态 TypeScript 环境文件 + .env 动态变量"的混合环境配置架构。你将掌握:环境文件的分工与切换机制、load-env.js如何把.env生成类型安全的env.generated.ts常量、getEnv系列工具函数的使用方式,以及完整的新增环境变量与安全实践流程,可直接用于开发、构建与集成调试。
一、环境配置的整体架构
Super Productivity 采用"混合式(hybrid)"环境配置方案,将两类信息分层管理:
- 静态基础配置:以
production、stage、version等标志为主,写在静态 TypeScript 文件中,随代码一起提交。 - 动态与敏感配置:以 API Key、访问令牌、WebDAV 账号等为主,保存在
.env文件中,通过构建脚本转换为 TypeScript 常量,不进入版本控制。
这套方案的核心动机在于:既有编译期可验证的静态标志,又能让密钥类信息与源码隔离,避免泄露。
1.1 静态环境文件(Static Environment Files)
仓库中存在三个静态环境文件:
| 文件 | 用途 | production | stage |
|---|---|---|---|
| src/environments/environment.ts | 开发环境配置 | false | false |
| src/environments/environment.prod.ts | 生产环境配置 | true | false |
| src/environments/environment.stage.ts | 预发布/灰度环境配置 | true | true |
三个文件的结构完全一致,均从package.json中读取版本号,例如开发环境文件的内容为:
// src/environments/environment.ts import pkg from '../../package.json'; export const environment = { production: false, stage: false, version: pkg.version, };从源码结构看,version字段直接引用根目录 package.json 中的版本号,保证应用版本与发布版本始终同步;production/stage标志则决定了 Angular 在构建与运行时是否启用生产模式优化(如 AOT、压缩、摇树等)。Angular 的--configuration选项负责在production、stage、默认开发配置之间做文件替换。
1.2 动态环境变量(Dynamic Environment Variables)
.env:存放面向所有环境的敏感/环境特定值,例如第三方服务令牌,默认不提交。- src/app/config/env.generated.ts:由脚本自动生成的 TypeScript 常量文件,已被 gitignore。
env.generated.ts的内容大致如下(由脚本生成,实际以生成结果为准):
// This file is auto-generated by tools/load-env.js // Do not modify directly - edit .env file instead export const ENV = { DROPBOX_API_KEY: 'your-api-key-here', GOOGLE_DRIVE_TOKEN: 'your-token-here', } as const; export type EnvVars = typeof ENV;二、环境搭建与接入步骤
2.1 创建 .env 文件
cp .env.example .env仓库根目录已提供 .env.example 模板,其中给出了五个示例键(均默认注释掉):
# GOOGLE_DRIVE_TOKEN=your-token-here # DROPBOX_API_KEY=your-api-key-here # UNSPLASH_KEY=your-api-key-here # WEBDAV_URL=https://your-webdav-server.com # WEBDAV_USERNAME=your-username # WEBDAV_PASSWORD=your-password2.2 写入实际变量值
# .env GOOGLE_DRIVE_TOKEN=your-token-here DROPBOX_API_KEY=your-api-key-here UNSPLASH_KEY=your-unsplash-access-key WEBDAV_URL=https://your-webdav-server.com WEBDAV_USERNAME=your-username WEBDAV_PASSWORD=your-password这些变量分别服务于 Google Drive 备份同步、Dropbox 同步、Unsplash 背景图以及 WebDAV 同步后端等能力。需要注意:.env中的值与OPTIONAL_ENV_KEYS列表(见下文)是取并集的关系,脚本会把.env文件里的所有键一并纳入。
2.3 在代码中访问环境变量
生成的常量支持直接访问与工具函数两种方式:
// 方式一:直接访问(类型安全) import { ENV } from './app/config/env.generated'; const googleToken = ENV.GOOGLE_DRIVE_TOKEN; // 方式二:通过工具函数(带类型安全) import { getEnv, getEnvOrDefault } from './app/util/env'; const googleToken = getEnv('GOOGLE_DRIVE_TOKEN'); const dropboxKey = getEnvOrDefault('DROPBOX_API_KEY', 'default-key');三、运行与构建命令
所有 npm 脚本都会在执行前自动调用load-env.js生成 TypeScript 常量:
# 开发模式(默认环境) npm run startFrontend # 生产配置 npm run startFrontend:prod # 预发布配置 npm run startFrontend:stage对应构建命令同样会先完成常量生成:
# 生产构建 npm run buildFrontend:prod:es6 # 预发布构建 npm run buildFrontend:stage:es6需要特别说明的是:所有命令共用同一个.env文件,环境之间的差异完全由 Angular 的production/stage配置标志控制。从 package.json 可以看到脚本层面的配合:
"env": "node ./tools/load-env.js", "serve": "node ./tools/load-env.js --ensure && ng serve", "serveProd": "node ./tools/load-env.js --ensure && ng serve --configuration production"--ensure参数对应 tools/load-env.js 中的"占位模式":当env.generated.ts尚不存在时,自动生成一个空占位文件(ENV = {} as const),从而保证即使没有.env,ng serve也能正常启动。
四、底层工作原理
4.1 load-env.js 的生成流程
tools/load-env.js 是整个机制的枢纽,其工作流程如下:
定义已知键:内置
REQUIRED_ENV_KEYS(当前为空数组)与OPTIONAL_ENV_KEYS两个白名单。OPTIONAL_ENV_KEYS包含 7 个已知可选项:const OPTIONAL_ENV_KEYS = [ 'UNSPLASH_KEY', 'UNSPLASH_CLIENT_ID', 'ONEDRIVE_CLIENT_ID', 'GOOGLE_DRIVE_TOKEN', 'DROPBOX_API_KEY', 'WEBDAV_URL', 'WEBDAV_USERNAME', 'WEBDAV_PASSWORD', ];合并变量来源:先收集系统进程环境变量中命中白名单的键,再通过
dotenv.config({ path: '.env' })解析根目录.env文件,用.env中的值覆盖同名键,并把.env中出现的所有其他键一并并入环境对象。生成 TypeScript 内容:对键名排序后生成
export const ENV = { ... } as const;与export type EnvVars = typeof ENV;,值中的反斜杠与单引号会被转义(\→\\,'→\'),防止破坏字符串字面量。仅在有变化时写入:对比现有文件内容,若相同则输出
up to date而不重复写入,避免不必要的文件系统改动与重编译。透传后续命令:若在
--之后传入命令,脚本会通过spawn以inherit模式透传执行,便于把生成步骤与后续构建命令串成一条调用链。
4.2 类型安全工具函数
src/app/util/env.ts 提供了一组纯函数封装,可在任意位置(包括 Angular 上下文之外)使用:
import { ENV } from '../config/env.generated'; // 获取变量值,未设置时返回 undefined export const getEnv = (key: keyof typeof ENV): string | undefined => { return ENV[key] || undefined; }; // 获取可选变量(键名不在 REQUIRED_ENV_KEYS 中时也允许访问) export const getEnvOptional = (key: string): string | undefined => { return (ENV as any)[key] || undefined; }; // 获取数值型变量,非法数值返回 undefined export const getEnvNumber = (key: keyof typeof ENV): number | undefined => { const value = getEnv(key); if (value === undefined) return undefined; const num = Number(value); return isNaN(num) ? undefined : num; }; // 获取全部环境变量对象 export const getAllEnv = (): typeof ENV => ENV;其中getEnv的形参类型为keyof typeof ENV,因此TypeScript 能感知全部可用键,编写代码时自动补全、误拼键名会直接编译报错——这就是"零process.env、全程类型安全"的关键。仓库中已有实际调用示例,例如 src/app/core/unsplash/unsplash.service.ts 第 48 行:
private readonly ACCESS_KEY = getEnvOptional('UNSPLASH_KEY');而 src/app/imex/sync/onedrive-auth-mode.const.ts 同样通过getEnvOptional读取 OneDrive 相关客户端配置,印证了该机制在第三方服务集成中的真实使用。
五、安全注意事项
- 绝不提交
.env:密钥一旦入库即视为泄露。仓库 .gitignore 已同时忽略.env、.env.*与src/app/config/env.generated.ts。 - 生成文件同样不入库:
env.generated.ts由脚本生成、被 gitignore,任何协作者都要在本地cp .env.example .env后重新生成。 - 编译期注入而非运行时暴露:密钥在构建时被编译进 bundle,不会以环境变量形式暴露在运行时进程环境中。
.env.example只放非敏感示例值:模板中的令牌均为占位符,用于说明键名与格式。
六、新增环境变量的完整流程
在
.env中添加新键:NEW_API_KEY=your-api-key-here运行任意构建/启动命令(或直接执行
npm run env),脚本会自动把它并入ENV,类型定义随之更新。以全类型安全方式使用:
import { ENV } from './app/config/env.generated'; const apiKey = ENV.NEW_API_KEY; // 或使用工具函数 import { getEnv } from './app/util/env'; const apiKey = getEnv('NEW_API_KEY'); // TypeScript 知道所有可用键
补充建议:若新变量属于"可选集成能力"(如某个插件或第三方服务开关),可将其加入load-env.js的OPTIONAL_ENV_KEYS白名单,便于脚本在启动日志中提示检测到/未检测到该变量;若属于"缺了就无法运行"的关键项,则应放入REQUIRED_ENV_KEYS,脚本在缺失时会直接抛错中断,把配置错误暴露在构建阶段而非运行时。
七、该方案的核心收益
- 类型安全:完整 TypeScript 支持与自动补全,键名错误在编译期即被拦截。
- 零运行时依赖:常量在构建期被编译进 bundle,运行时不需要
process.env或额外的 webpack 配置。 - 跨平台通用:浏览器、Electron 容器、PWA 等环境均直接 import 使用。
- 简单直观:复制模板、填入变量、import 使用,三步即完成接入。
- 安全隔离:敏感信息只存在于
.env,绝不进入版本控制,生成文件亦被 gitignore。
该方案在开发期、CI 与发布流水线中均可复用:本地开发直接使用.env,CI 通过注入系统环境变量 +.env覆盖机制可灵活配置集成测试所需令牌;生产与预发布构建则依靠environment.prod.ts/environment.stage.ts的静态标志完成行为差异控制,最终形成一套"静态标志管行为、动态变量管密钥"的清晰职责边界。
【免费下载链接】super-productivitySuper Productivity is an advanced todo list app with integrated Timeboxing and time tracking capabilities. It also comes with integrations for Jira, GitLab, GitHub and Open Project.项目地址: https://gitcode.com/GitHub_Trending/su/super-productivity
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考