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
檔案來切換環境,這樣的方式肯定是不行的,所以我開始尋找解決方案。
參考 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
環境生效,這一功能是針對 dev
和 preview
的,經過我的測試,dev
環境確實沒有問題。
根據這兩個 issue,我推論環境變數要在啟動專案時新增環境變數,於是我根據檔案設定 pm2,使用 source .env && node .output/server/index.mjs
啟動,結果仍然無法讀取環境變數。
這裡稍微跑個題,雖然沒能通過 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 的紀錄檔預設是合在一起的,時間久或者存取量大的情況下,紀錄檔會非常大,不方便檢視,我們可以將紀錄檔分開,這樣會更方便通過紀錄檔檢視專案的啟動情況。
$ pm2 install 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
變數中,但是經過我的測試,這個功能並沒有生效,不知道是不是哪裡有錯,如果有人知道,希望能告訴我。
新框架總是有很多坑,在開發過程中我遇到了很多問題,但是都一一解決了,雖然過程很痛苦,但是收穫也很大,希望這篇文章能幫助到和我遇到同樣問題的人,也為自己做一個記錄。