Nuxt3環境變數配置

夜盡丶發表於2023-04-28

Nuxt3 正式釋出還不到半年,在投入生產環境使用後,遇到了不少問題,很難找到合適的解決方案,其中環境變數配置就是其中一個,之前一直未能解決,最近要上持續整合,無法繞過這個問題,所以花了點時間研究了一下,最終找到了解決方案,記錄一下。

官方文件

面對一個新框架,我們自然是希望官方文件能夠詳細地說明使用方式,最初開始使用的時候,並沒有關於環境變數配置的說明,甚至可能沒有相關功能,不過隨著版本的更新,文件中新增了相關的內容,但是安裝文件的說明進行配置後並沒有生效,後來發現有些是理解偏差,另外還有一些問題我仍然沒有解決,不過目前的配置方式已經滿足我們的需求,所以這裡記錄一下研究配置的過程。

關於配置的官網文件: Runtime Config

用於持續整合的命令: nuxi build

環境變數檔案的說明: .env

配置檔案說明: Nuxt Config

基礎變數配置

最開始我按照官方文件的說明將環境變數配置在了 nuxt.config.ts 檔案中,使用起來還是很簡單的。

// nuxt.config.ts
runtimeConfig: {
  apiKey: ""; // Default to an empty string, automatically set at runtime using process.env.NUXT_API_KEY
  public: {
    baseURL: ""; // Exposed to the frontend as well.
  }
}

// 使用
const config = useRuntimeConfig();
const baseUrl = config.public.baseUrl;

需要暴露在前端的變數寫在 public 中,不需要暴露的直接寫在 runtimeConfig 下,呼叫也很方便,但是作為環境變數,僅滿足一種環境肯定是不行的,最初我是透過 process.env.NODE_ENV 來判斷當前環境,然後根據環境來配置不同的變數,然而上線之後遇到了一個問題,測試環境和正式環境的 NODE_ENV 都是 production ,這樣就導致了無法區分環境,所以在很長一段時間內,我都是透過手動修改 nuxt.config.ts 檔案來切換環境,這樣的方式肯定是不行的,所以我開始尋找解決方案。

.env 檔案配置

參考 Vue Cli 專案,每次執行 run build 的時候,自動讀取對應的 .env 檔案中的變數進行編譯,這樣就是我的理想情況,而且我發現官方的文件中後來確實新增了相關的內容,我就想當然地進行了配置,結果卻沒有生效。

"scripts": {
  "build": "nuxi build --dotenv .env.production",
  "test": "nuxi build --dotenv .env.test",
  "dev": "nuxi dev --dotenv .env.development -p 3001",
  "generate": "nuxi generate",
  "preview": "nuxi preview",
  "start": "node .output/server/index.mjs"
},

在這裡提前說明一下,這個其實就是最終的解決方案,這種寫法現在可以生效,早期版本的時候並不支援。

最開始因為文件不完善,使用的人也不多,我只能在官方 GitHub 的 Issues 中尋找有沒有類似的問題,因為畢竟是個很常見的需求,當時找到了一些和我有同樣困惑的例子。

--dotenv config doesn’t work in production environment

Deployment Nuxt Application on Linux KO

按照作者的回答,我的理解是 .env 檔案並不能在 build 環境生效,這一功能是針對 devpreview的,經過我的測試,dev 環境確實沒有問題。

根據這兩個 issue,我推論環境變數要在啟動專案時新增環境變數,於是我根據文件配置 pm2,使用 source .env && node .output/server/index.mjs 啟動,結果仍然無法讀取環境變數。

pm2 配置

這裡稍微跑個題,雖然沒能透過 pm2 成功配置環境變數,但是看到了 pm2 的環境變數配置相關的一些文件,順便對專案中的配置進行了一些最佳化。

module.exports = {
  apps: [
    {
      name: "xxx",
      exec_mode: "cluster",
      instances: "max", // Or a number of instances
      script: "npm",
      args: "start",
      error: "/root/.pm2/logs/xxx-error.log", // 放在系統盤下面-錯誤日誌路徑如果不想使用日誌 "/root/null"
      output: "/root/.pm2/logs/xxx-out.log", // 放在系統盤下面-正常日誌路徑如果不想使用日誌 "/root/null"
      combine_logs: false,
      merge_logs: false,
      log_date_format: "YYYY-MM-DD HH:mm Z", // 指定日誌檔案的時間格式
      env: {
        NODE_ENV: "production",
        PORT: 3001,
        FRONTEND_VERSION: "v1.0.0",
        FRONTEND_CLIENTID: "xxx",
      },
      env_production: {
        NODE_ENV: "development",
        PORT: 3000,
        FRONTEND_VERSION: "v1.0.0",
        FRONTEND_CLIENTID: "xxx",
      },
    },
  ],
};

環境變數

一開始我以為 pm2 配置中的 env 是專案中環境變數,設定值之後測試仍然沒有效果,仔細檢視文件後發現 env 中的欄位是固定的,主要是用於啟動程式的配置,可以配置不同環境的啟動埠和 node 的環境值等。詳情可以看文件 環境變數

日誌

pm2 的日誌預設是合在一起的,時間久或者訪問量大的情況下,日誌會非常大,不方便檢視,我們可以將日誌分開,這樣會更方便透過日誌檢視專案的啟動情況。

  1. 安裝 pm2-logrotate
$ pm2 install pm2-logrotate
  1. 配置 pm2-logrotate
// 設定日誌檔案的最大大小,超過這個大小就會進行分割
pm2 set pm2-logrotate:max_size 4M
// 設定壓縮日誌檔案
pm2 set pm2-logrotate:compress true

透過各種配置可以把日誌調整到滿足我們需求的形式,詳情可以看文件 pm2-logrotate

正確的配置

多次嘗試以失敗告終之後,我暫時擱置了環境變數的配置問題,但是最近要做持續整合,肯定要透過指令而不是人為修改來完成,所以我又針對環境變數配置進行了測試,最終找到了正確的配置方式。

正如上面 .env 檔案配置 一節所說,最終的解決方案就是在 package.json 中配置 run build 的時候指定 .env 檔案,這樣就可以在編譯的時候讀取對應的環境變數。不確定是不是最近 nuxt 的版本更新了,這個功能才生效,還是說之前的版本我沒有配置正確,總之現在是可以用了。

完整的配置如下:

// package.json
"scripts": {
  "build": "nuxi build --dotenv .env.production",
  "test": "nuxi build --dotenv .env.test",
  "dev": "nuxi dev --dotenv .env.development -p 3001",
  "generate": "nuxi generate",
  "preview": "nuxi preview",
  "start": "node .output/server/index.mjs"
},
// .env.production
NUXT_API_KEY=https://xxx
NUXT_PUBLIC_BASE_URL=https://xxx
// nuxt.config.ts
runtimeConfig: {
  apiKey: process.env.NUXT_API_KEY; // Default to an empty string, automatically set at runtime using process.env.NUXT_API_KEY
  public: {
    baseURL: process.env.NUXT_PUBLIC_BASE_URL; // Exposed to the frontend as well.
  }
}

// 使用
const config = useRuntimeConfig();
const baseUrl = config.public.baseUrl;

但是,問題並沒有完全解決,還存在一個問題和一個疑點。

先說問題,Nuxt 的環境變數是在服務端執行的,在客戶端並不能獲取到環境變數,我這裡主要是用於判斷環境使用不同的 key 值,process.env.NUXT_PUBLIC_PAGE_WWW 前面還是 process.env,我的理解是這個值的獲取是基於 node 的,客戶端無法正確讀取,所以我在 .env 檔案中增加了一個新的變數 VITE_NUXT_ENV=test 用於判斷環境,這樣就可以在客戶端透過 import.meta.env.VITE_NUXT_ENV 獲取到環境變數。

感覺上這樣的思路存在一些問題,如果有更好的解決方案,歡迎指教或討論。

而疑點就在註釋裡,前面應該也有人注意到,官網文件的註釋裡寫到:

Default to an empty string, automatically set at runtime using process.env.NUXT_API_KEY

意思應該是如果在 runtimeConfig 設定了環境變數,也會自動新增到對應的 process.env 變數中,但是經過我的測試,這個功能並沒有生效,不知道是不是哪裡有錯,如果有人知道,希望能告訴我。

總結

新框架總是有很多坑,在開發過程中我遇到了很多問題,但是都一一解決了,雖然過程很痛苦,但是收穫也很大,希望這篇文章能幫助到和我遇到同樣問題的人,也為自己做一個記錄。

相關文章