Next.js 因其對環境變數的處理而受到臭名昭著的批評。儘管多年來它已經得到了改進,但它仍然存在一些可能令人困惑的怪癖,特別是當涉及到透過 process.env 物件公開的公共變數時。
所有 NEXT_PUBLIC_ 前綴的變數都是客戶端可存取的,但正如官方文件中提到的,它們僅在建置時可用,因此與 Docker 一起使用時無法在執行時間變更。
雖然我過去已經討論過這個問題,但我發現了另一種可能有趣的探索方法,即使用可以在運行時更改的全域共享變數。
Next.js 部分支援全域變量,但我們可以透過一個小的注入腳本使用 polyfill 來使它們可用。
// app/layout.tsx <script dangerouslySetInnerHTML={{ __html: `!function(t){function e(){var e=this||self;e.globalThis=e,delete t.prototype._T_}"object"!=typeof globalThis&&(this?e():(t.defineProperty(t.prototype,"_T_",{configurable:!0,get:e}),_T_))}(Object);`, }} />
原始程式碼取自GitHub上的這個評論,它基本上創建了一個全域的globalThis對象,可用於在客戶端和伺服器之間共用變數。 Polyfill 隨 Next.js 14.x 一起提供,但它似乎破壞了某些瀏覽器中的 globalThis 對象,這就是我們使用 __html 屬性將程式碼直接注入頁面的原因。
接下來,我們可以使用 Zod 在運行時驗證變量,如果變量無效則拋出錯誤。此步驟對於確保變數始終可用且有效、避免執行時間錯誤以及使應用程式面臨安全問題至關重要。
npm install zod
然後我們建立一個變數.ts 文件,其中包含一些實用函數,用於從 process.env 中獲取變數並將它們安全地轉換為預期類型。
// lib/env.ts import { z } from 'zod'; // Load the variables export const load = () => { return process.env; }; // Parse or throw the variables export function parseOrThrow(schema: z.Schema, data: unknown, error: Error) { const parsed = schema.safeParse(data); // Log the errror if (parsed.success) return parsed.data; console.error(parsed.error); throw error; } // Some zod helpers to use export const port = z .string() .refine( (port) => parseInt(port) > 0 && parseInt(port) < 65536, 'Invalid port number' ); export const str = z.string(); export const url = z.string().url(); export const num = z.coerce.number(); export const bool = z.coerce.boolean();
load 是一個傳回 process.env 物件的簡單函數,而 parseOrThrow 是一個實用函數,它使用 Zod 模式解析變量,並在變數無效時拋出錯誤。
最後,我們可以建立一個變數.ts 文件,其中包含變數架構以及用於載入和解析變數的實用函數。
// lib/vars.ts import { z } from 'zod'; import { load, parseOrThrow } from './env'; import { parseOrThrow, load, str, num, bool, port } from './env'; // Define the variables schema const schema = z.object({ PUBLIC_VARIABLE: str.optional(), PUBLIC_MY_NUM: num, PUBLIC_BOOL: bool, PUBLIC_PORT: port, }); export const loadEnv = () => { const env = load(); const parsed = parseOrThrow(schema, env, new Error('Invalid variables')); for (const key of Object.keys(parsed)) { if (!globalThis[key]) { globalThis[key] = parsed[key]; } } };
為了使用變量,我們需要先載入它們。我們可以在 app/layout.tsx 檔案或任何其他佈局檔案中執行此操作,以逐漸將它們暴露給應用程式的其餘部分,無論是在客戶端還是在伺服器上。
// app/layout.tsx import { loadEnv } from '@/lib/vars'; loadEnv(); export default function RootLayout({ children, }: { children: React.ReactNode; }) { return ( <html lang="en"> <body>{children}</body> </html> ); }
以上是Next.js 公共變數的詳細內容。更多資訊請關注PHP中文網其他相關文章!