Skip to main content
跨瀏覽器、跨環境使用同一份擴充功能程式碼,不必把值寫死。

瀏覽器擴充功能中的環境變數

瀏覽器擴充功能會在使用者的機器上執行程式碼。這也讓「設定」與「機密」之間的界線比一般網頁應用更為敏感:任何安裝你擴充功能的人,都能讀出你打包到 JavaScript、HTML 或編譯後 manifest.json 中的內容。 Extension.js 用兩種環境載入路徑來處理這件事:一種服務 編譯後的擴充功能 bundle(會感知瀏覽器與模式),另一種則是在 Node 中載入 extension.config.* 時使用。 兩條路徑都重要,端視你要從哪裡讀取變數。

範本範例

new-env

new-env template screenshot 透過一個新分頁擴充功能(讀取 EXTENSION_PUBLIC_* 值)實際看看環境變數如何運作。
儲存庫:extension-js/examples/new-env

content-env

content-env template screenshot 在注入網頁的 content script 中使用環境變數。
儲存庫:extension-js/examples/content-env

運作方式

擴充功能 bundle(編譯時)

建置擴充功能時,編譯器會從你的擴充功能套件資料夾,依下列順序挑選 一個 第一個符合的 env 檔:
  1. .env.[browser].[mode](例如 .env.chrome.development
  2. .env.[browser]
  3. 上述兩項的引擎家族變體(見下方說明)
  4. .env.[mode]
  5. .env.local
  6. .env
瀏覽器範圍的檔案依 引擎家族 解析,與瀏覽器專屬 manifest 欄位一致。在精確的瀏覽器名稱之後,Chromium 家族目標(chromiumchromeedgechromium-based、各 Chromium 分支以及 Safari 建置)還會依序嘗試 .env.chromium.env.chrome.env.edge.env.chromium-based(各自的 .[mode] 變體優先)。Gecko 家族目標還會嘗試 .env.firefox.env.gecko-based。因此一份 .env.chromium 檔即可涵蓋 chromeedgebrave 建置,而精確的瀏覽器檔案永遠優先於家族檔案。 .env.defaults 存在時,Extension.js 一定會先合併 它,再合併所選檔案的變數。最後,系統的 process.env 對重疊鍵擁有最高優先權。
選擇的是上述清單中的 單一 env 檔(再加上 .env.defaults), 不會逐檔串聯合併。.env.example 僅作為文件範例,永遠不會被當成值來源載入。
如果專案旁存在 env 檔,但沒有任何一個符合目前的瀏覽器與模式,建置會印出警告,列出找到的檔案與查找過的候選檔名。這樣瀏覽器後綴的拼字錯誤會立刻暴露,而不是在沒有值注入的情況下悄悄完成建置。 如果專案旁邊找不到符合的檔案,Extension.js 會從 最近的 workspace 根目錄 重複相同的搜尋;該根目錄是最近一個包含 pnpm-workspace.yaml 的祖先資料夾。Monorepo 中的擴充功能可以共用根層的 env 檔,用於 bundle 注入。 Monorepo 限制: workspace fallback 只有在祖先目錄存在 pnpm-workspace.yaml 標記時才會執行。如果你使用純 npm 或 Yarn workspaces,只靠 package.json"workspaces" 欄位,這套自動往上找的機制就不適用。這種情況下,請把 env 檔放在擴充功能套件旁邊,或在儲存庫根目錄加上 pnpm-workspace.yaml

extension.config.*(Node,在你的設定執行前)

extension.config.js / .mjs / .cjs 會在 Node 中執行。Extension.js 在評估該檔案前,會預先載入 一小組 檔案到 process.env,讓設定檔可以讀取:
  1. .env.defaults(存在時會合併)
  2. 接著從 .env.development.env.local.env 中載入 第一個 存在的檔案
此預載步驟 不會 使用 .env.chrome.env.chrome.development 等瀏覽器範圍的檔案。如果有需要,請透過 shell 或持續整合(CI)流水線中的 process.env 設定值。 你也可以依賴 bundle 階段的 env(上述章節說明),把 EXTENSION_PUBLIC_* 的值注入到擴充功能程式碼裡。 Workspace fallback: 如果擴充功能套件資料夾中都沒有這些檔案,相同的預載會從最近一個包含 pnpm-workspace.yaml 的資料夾執行(限制與上方相同)。 之所以分成兩條路徑,是因為設定檔載入時還不知道目前瀏覽器,但 bundler 已經知道當前的瀏覽器與模式。

內建環境變數

Extension.js 會在編譯時注入內建變數,讓你的擴充功能程式碼隨時都能取得瀏覽器與模式資訊。 以上所有內建變數,皆可透過 process.env.*import.meta.env.* 取得。

環境變數總覽

公開/執行階段變數(使用者定義)

靜態佔位符變數

內建/別名變數

CLI 與開發伺服器的操作變數

遙測控制變數

完整的退出條款請參閱 遙測與隱私

瀏覽器傳輸調校變數

這些變數會覆寫 Chrome DevTools Protocol(CDP)與 Remote Debugging Protocol(RDP)內部逾時設定。對於緩慢的持續整合(CI)環境、Docker 容器,或除錯不穩定的瀏覽器連線特別有用。

瀏覽器專屬環境變數

下列規則適用於 編譯/bundle 時 的 env 選擇(請見上方的 擴充功能 bundle)。它們 不適用 於 Node 中那一小段 extension.config.* 的預載。 需要在不同瀏覽器使用不同的值?Extension.js 支援帶有瀏覽器範圍的 env 檔,例如 .env.chrome(Chrome 擴充功能環境變數)與 .env.firefox(Firefox 擴充功能環境變數)。你也可以結合瀏覽器與模式,對應到單一建置變體:
  • .env.chrome.development:僅在以 development 模式於 Chrome 執行擴充功能時套用。
  • .env.firefox.production:僅在以 production 模式為 Firefox 建置擴充功能時套用。
優先順序為:
  • .env.[browser].[mode]
  • .env.[browser]
  • 引擎家族變體(例如任何 Chromium 家族目標都會符合 .env.chromium
  • .env.[mode]
  • .env.local
  • .env
你很少需要為每個廠商各建一個檔案。由於解析依引擎家族進行,.env.chromium 即可涵蓋 chromeedge 及各 Chromium 分支,.env.firefox 則涵蓋各 Gecko 分支。只有當某個目標需要與家族其他成員不同的值時,才加上精確的瀏覽器檔案(例如 .env.edge)。

範例檔案

自訂環境變數

你可以在專案根目錄的 env 檔中定義自訂變數。
Extension.js 只會把以 EXTENSION_PUBLIC_ 為前綴的變數注入到 JavaScript bundle(process.env / import.meta.env)。
重要: Extension.js 不會把沒有 EXTENSION_PUBLIC_ 前綴的變數注入 JS bundle。
不過,輸出的 .json.html 檔中的佔位符可以解析 $EXTENSION_* 標記,所以請避免在靜態資產樣板中引用機密。

使用環境變數

你可以在 manifest.json、語系檔、HTML 與 JavaScript/TypeScript 檔案中使用環境變數。

1. 在 manifest.json

manifest.json 本身不支援環境變數,但 Extension.js 會在建置時替換掉受支援的佔位符。例如:
編譯時,Extension.js 會把 $EXTENSION_PUBLIC_API_KEY 取代為解析後的 env 值。

2. 在語系檔中

當值需要隨環境改變時,你也可以在語系檔中使用佔位符。例如:
當 Extension.js 輸出資產時,會把 $EXTENSION_PUBLIC_SITE_URL 等佔位符替換為解析後的值。

3. 在 HTML 檔中

你也可以在靜態 HTML 檔中使用佔位符(例如放在 pages/ 下):
編譯時,Extension.js 會替換輸出 HTML 中的 $EXTENSION_PUBLIC_API_KEY

4. 在 JSX 元件中

在 React/JSX/TS 檔案中,可以用 process.env 讀取 env 值:
Extension.js 會在編譯時將這些值內嵌進來,並可隨瀏覽器/模式變化。

import.meta 支援

針對 ECMAScript Module(ESM)工作流程,Extension.js 也支援 import.meta.env
service_worker.mjs
對於已注入的 env 鍵,import.meta.envprocess.env 內容一致。 讀取任何 env 檔都未定義的鍵會得到 undefined,而不會當機:Extension.js 會把裸的 import.meta.env 定義為包含所有已注入變數的物件(與 Vite 行為一致),因此 import.meta.env.MISSING_KEYconst {FOO} = import.meta.env 這類解構在任何輸出格式下都是安全的。

擴充功能建置中的機密

瀏覽器擴充功能會在使用者的機器上執行。任何安裝你擴充功能的人,都可以檢視你打包進 JavaScript、HTML 或編譯後 manifest.json 的任何值。請把 bundle 視為公開內容。 幾條原則:
  • 絕對不要把 API 機密、簽章金鑰或驗證權杖放在 EXTENSION_PUBLIC_* 之後。這個前綴是為了標記「可安全發佈」,不是為了「隱藏」。
  • 機密值不要放進靜態 manifest.json、語系或 HTML 樣板中的 $EXTENSION_* 佔位符,因為它們會在建置時展開並寫進產物。
  • 任何特權操作都應交由你自己的後端處理,再從擴充功能在執行階段呼叫它。
  • 持續整合(CI)時,把建置機密放在 process.env(不要提交 env 檔),共用的安全預設值才用 .env.defaults

最佳實務

  • 只暴露必須發佈的值: 只有對使用者端安全的鍵才以 EXTENSION_PUBLIC_ 為前綴。
  • .env.defaults 提供共用預設: 保持團隊預設值可預期,同時允許本機/系統覆寫。
  • 機密不要放進靜態佔位符: 避免在 HTML/JSON 樣板中放入機密的 $EXTENSION_* 標記。
  • 版本控管乾淨: 提交 .env.example 作為文件(建置永遠不會把它當成值來源讀取),並忽略真正的 env 檔(.env.env.local、瀏覽器/模式變體)。

下一步