让同一个扩展代码库在不同浏览器与环境间运行,而不必硬编码任何值。
浏览器扩展中的环境变量
浏览器扩展会发布到用户机器上运行。这让配置与密钥之间的界线比普通 Web 应用更加严格。任何安装你扩展的人都能读取你打进 JavaScript、HTML 或编译后的 manifest.json 中的所有内容。
Extension.js 通过两种环境加载路径来处理这件事。一种用于编译后的扩展产物(感知浏览器与模式)。另一种用于在 Node 中加载 extension.config.*。
两条路径都很重要,取决于你在哪里读取变量。
模板示例
new-env
通过一个读取 EXTENSION_PUBLIC_* 值的新标签页扩展,看看环境变量是如何工作的。
npx extension@latest create my-extension --template=new-env
代码仓库:extension-js/examples/new-env
content-env
在注入到网页中的 content script 内使用环境变量。
npx extension@latest create my-extension --template=content-env
代码仓库:extension-js/examples/content-env
工作原理
扩展产物(编译时)
构建扩展时,编译器会按以下顺序在扩展包目录中选择一份与之匹配的 env 文件:
.env.[browser].[mode](例如 .env.chrome.development)
.env.[browser]
.env.[mode]
.env.local
.env
.env.example
如果存在 .env.defaults,Extension.js 总是先合并它,然后再合并所选文件的变量。最终,系统 process.env 对重叠的键拥有最高优先级。
选择的是上述列表中的单个 env 文件(加上 .env.defaults),而不是对所有文件做完整级联。
如果项目旁找不到匹配文件,Extension.js 会从最近的 workspace 根目录重复同样的搜索。所谓 workspace 根目录,是离当前位置最近、包含 pnpm-workspace.yaml 的祖先目录。位于 monorepo 中的扩展可以共享根级 env 文件来注入产物。
Monorepo 约束: 仅当祖先目录中存在 pnpm-workspace.yaml 这个标记文件时,workspace 回退才会执行。如果你使用的是仅依赖 package.json "workspaces" 字段的纯 npm 或 Yarn workspaces,则不会触发自动根目录查找。这种情况下,请把 env 文件放在扩展包旁边,或在仓库根目录加上 pnpm-workspace.yaml。
extension.config.*(Node 中,在你的配置运行之前)
extension.config.js / .mjs / .cjs 在 Node 中运行。在 Extension.js 求值该文件之前,会预先把一小组文件加载进 process.env,以便配置可以读取它们:
.env.defaults(存在时合并)
- 然后是以下文件中首个存在的那个:
.env.development、.env.local、.env
这一预加载步骤不会使用浏览器作用域的文件,比如 .env.chrome 或 .env.chrome.development。需要时,请通过 shell 或持续集成 (CI) 流水线中的普通 process.env 设置。
你也可以依赖编译期的产物 env(上文已说明) 将 EXTENSION_PUBLIC_* 注入扩展代码。
Workspace 回退: 如果扩展包目录中找不到上述任何文件,则会在最近的、包含 pnpm-workspace.yaml 的目录中执行同样的预加载(约束与上文一致)。
之所以分成两条路径,是因为读取配置文件时与浏览器无关,而打包器知道当前的浏览器与模式。
内置环境变量
Extension.js 在编译时注入内置变量,因此你总能在扩展代码里拿到当前的浏览器与模式。
| 变量名 | 说明 |
|---|
EXTENSION_PUBLIC_BROWSER | 当前扩展的浏览器目标(例如 chrome、firefox、edge)。 |
EXTENSION_PUBLIC_MODE | 扩展当前运行的模式,比如 development 或 production。 |
EXTENSION_BROWSER | 浏览器目标(非 legacy 别名)。 |
EXTENSION_MODE | 构建模式(非 legacy 别名)。 |
BROWSER | 浏览器目标的短别名。 |
MODE | 模式的短别名。 |
NODE_ENV | 与编译器模式对齐的 Node 环境(development / production)。 |
上述所有内置变量同时可通过 process.env.* 与 import.meta.env.* 读取。
环境变量清单
公开/运行时变量(用户定义)
| 变量样式 | 用途 | 在 JS 运行时可用 | 备注 |
|---|
EXTENSION_PUBLIC_* | 把用户定义的值暴露给扩展代码 | 是(process.env + import.meta.env) | 只能用于可安全发布的值 |
静态占位符变量
| 变量样式 | 用途 | 在 JS 运行时可用 | 备注 |
|---|
输出的 .html / .json 中的 $EXTENSION_* 标记 | 静态资产中的构建期占位符替换 | 不作为 JS 变量 | 不要在静态模板中使用密钥 |
内置/别名变量
| 变量 | 类型 | 备注 |
|---|
EXTENSION_PUBLIC_BROWSER | 内置 | 浏览器目标 |
EXTENSION_PUBLIC_MODE | 内置 | 构建模式 |
EXTENSION_BROWSER | 内置别名 | 浏览器目标 |
EXTENSION_MODE | 内置别名 | 构建模式 |
BROWSER | 内置别名 | 浏览器短名 |
MODE | 内置别名 | 模式短名 |
NODE_ENV | 内置 | 编译器模式 |
CLI 与开发服务器运行控制变量
| 变量 | 用途 | 典型用法 |
|---|
EXTENSION_AUTO_EXIT_MS | N 毫秒后自动退出 dev 进程 | CI 中的强制停止控制 |
EXTENSION_FORCE_KILL_MS | 强制 kill 超时兜底 | CI 清理的健壮性 |
EXTENSION_VERBOSE | 在所选流程中输出详细诊断 | 调试 CLI 行为 |
EXTENSION_AUTHOR_MODE | 维护者/作者诊断模式 | 内部诊断与工具链 |
EXTENSION_CLI_NO_BROWSER | 在 CLI 中禁用浏览器启动(设为 1) | 等价于 --no-browser 标志 |
EXTENSION_DEV_NO_BROWSER | 在开发服务器中禁用浏览器启动 | Monorepo 中不启动浏览器只监听 |
EXTENSION_NO_RELOAD | 跳过开发模式下的 content-script 重载运行时 | 等价于 extension dev --no-reload |
EXTENSION_DEV_DRY_RUN | 跳过开发服务器启动(提前返回) | 烟雾测试 CLI 接线 |
EXT_BROWSERS_CACHE_DIR | 覆盖托管浏览器缓存目录 | 自定义 CI 缓存路径 |
遥测控制变量
| 变量 | 用途 | 默认值 |
|---|
EXTENSION_TELEMETRY_DISABLED | 完全禁用遥测(设为 1) | 未设置 |
EXTENSION_TELEMETRY | 向后兼容的禁用(设为 0 禁用) | 未设置 |
EXTENSION_TELEMETRY_SAMPLE_RATE | command_executed 的采样率(0.0–1.0) | 0.2 |
EXTENSION_TELEMETRY_MAX_EVENTS | 单个 CLI 进程发出的事件上限 | 3 |
EXTENSION_TELEMETRY_DEBOUNCE_MS | 相同事件元组的去重窗口(毫秒) | 60000 |
EXTENSION_TELEMETRY_TIMEOUT_MS | 遥测请求的 HTTP 超时(毫秒) | 300 |
EXTENSION_TELEMETRY_DEBUG | 将遥测载荷打印到 stderr(设为 1) | 未设置 |
完整退出契约请参见遥测与隐私。
浏览器传输调优变量
这些变量会覆盖内部 Chrome DevTools Protocol (CDP) 与 Remote Debugging Protocol (RDP) 的超时设置。它们对慢速持续集成 (CI) 环境、Docker 容器或调试不稳定的浏览器连接很有用。
| 变量 | 用途 | 默认值 |
|---|
EXTENSION_CDP_COMMAND_TIMEOUT_MS | CDP sendCommand 超时(毫秒) | 12000 |
EXTENSION_CDP_HTTP_TIMEOUT_MS | CDP HTTP /json 发现超时(毫秒) | 1200 |
EXTENSION_CDP_HEARTBEAT_INTERVAL_MS | CDP WebSocket 心跳间隔(毫秒) | 30000 |
EXTENSION_RDP_EVAL_TIMEOUT_MS | Firefox RDP 求值超时(毫秒) | 8000 |
EXTENSION_RDP_MAX_RETRIES | Firefox RDP 连接重试次数 | 150 |
EXTENSION_RDP_RETRY_INTERVAL_MS | Firefox RDP 连接重试间隔(毫秒) | 1000 |
浏览器特定的环境变量
以下规则适用于编译期/产物 env 选择(见上文扩展产物)。它们不适用于 Node 中针对 extension.config.* 的窄范围预加载。
需要为每个浏览器使用不同的值吗?Extension.js 支持浏览器作用域的 env 文件,比如 .env.chrome(Chrome 扩展环境变量) 与 .env.firefox(Firefox 扩展环境变量)。你也可以把浏览器和模式组合起来,针对单个构建变体:
.env.chrome.development:只有在以开发模式运行 Chrome 时,Extension.js 才应用它。
.env.firefox.production:只有在以生产模式构建 Firefox 时,Extension.js 才应用它。
优先级顺序:
.env.[browser].[mode]
.env.[browser]
.env.[mode]
.env.local
.env
.env.example
示例文件
# .env.chrome.development
EXTENSION_PUBLIC_API_URL=https://api-dev.chrome.com
# .env.firefox.production
EXTENSION_PUBLIC_API_URL=https://api.firefox.com
自定义环境变量
你可以在项目根目录的 env 文件中定义自定义变量。
Extension.js 只会把以 EXTENSION_PUBLIC_ 为前缀的变量注入 JavaScript 产物(process.env / import.meta.env)。
# .env
EXTENSION_PUBLIC_API_KEY=your_api_key_here
EXTENSION_PUBLIC_SITE_URL=https://example.com
PRIVATE_KEY=abc123 # Not injected into JS bundles
重要: Extension.js 不会把没有 EXTENSION_PUBLIC_ 前缀的变量注入 JS 产物。
但输出的 .json / .html 文件中的占位符可以解析 $EXTENSION_* 标记,所以不要在静态资产模板里引用密钥。
使用环境变量
你可以在 manifest.json、locale 文件、HTML、JavaScript/TypeScript 文件中使用环境变量。
1. 在 manifest.json 中
manifest.json 本身不支持环境变量,但 Extension.js 会在构建期替换支持的占位符。例如:
{
"name": "My Extension",
"version": "1.0",
"description": "This extension is connected to $EXTENSION_PUBLIC_API_KEY",
"background": {
"service_worker": "service_worker.js"
}
}
编译时,Extension.js 会把 $EXTENSION_PUBLIC_API_KEY 替换为解析后的 env 值。
2. 在 locale 文件中
当值应当随环境而变时,也可以在 locale 文件中使用占位符。例如:
{
"appName": {
"message": "My Extension - $EXTENSION_PUBLIC_SITE_URL"
},
"appDescription": {
"message": "Connected to API at $EXTENSION_PUBLIC_API_KEY"
}
}
Extension.js 在输出资产时,会把 $EXTENSION_PUBLIC_SITE_URL 这样的占位符替换为解析后的值。
3. 在 HTML 文件中
也可以在静态 HTML 文件中(例如 pages/ 下)使用占位符:
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>My Extension</title>
</head>
<body>
<h1>Welcome to My Extension</h1>
<p>API Key: $EXTENSION_PUBLIC_API_KEY</p>
</body>
</html>
编译时,Extension.js 会替换输出 HTML 中的 $EXTENSION_PUBLIC_API_KEY。
4. 在 JSX 组件中
在 React/JSX/TS 文件中,用 process.env 读取 env 值:
const ApiInfo = () => {
const apiUrl = process.env.EXTENSION_PUBLIC_SITE_URL;
const apiKey = process.env.EXTENSION_PUBLIC_API_KEY;
return (
<div>
<h1>API Information</h1>
<p>URL: {apiUrl}</p>
<p>Key: {apiKey}</p>
</div>
);
};
export default ApiInfo;
Extension.js 会在编译期内联这些值,并且可以按浏览器/模式而变化。
对于 ECMAScript Module (ESM) 工作流,Extension.js 同样支持 import.meta.env:
const apiUrl = import.meta.env.EXTENSION_PUBLIC_API_URL;
console.log(`API URL for the current environment: ${apiUrl}`);
对于注入的 env 键,import.meta.env 与 process.env 是对等的。
浏览器扩展构建中的密钥
浏览器扩展会在用户的机器上运行。任何安装了你的扩展的人都能查看你打进 JavaScript、HTML 或编译后的 manifest.json 中的任意值。请把扩展包视为公开内容。
几条规则要遵守:
- 永远不要把 API 密钥、签名密钥或鉴权令牌放在
EXTENSION_PUBLIC_* 后面。这个前缀意味着该值可以安全发布,而不是用来隐藏它。
- 不要在静态
manifest.json、locale 或 HTML 模板中放敏感值对应的 $EXTENSION_* 占位符。它们会在构建期展开,最终出现在产物里。
- 把任何特权操作放到你自己的后端,扩展在运行时再调用它。
- 对于持续集成 (CI),把构建期密钥放在
process.env(不要提交 env 文件),并把可共享的安全默认值放在 .env.defaults。
最佳实践
- 只暴露必须发布的值: 只给客户端安全的键加
EXTENSION_PUBLIC_ 前缀。
- 用
.env.defaults 共享团队默认值: 在保留可预测默认值的同时,允许本地/系统覆盖。
- 不要把密钥放进静态占位符: 避免在 HTML/JSON 模板里放敏感的
$EXTENSION_* 标记。
- 版本控制卫生: 提交
.env.example,忽略真实的 env 文件(.env、.env.local、浏览器/模式变体)。
下一步