Next.js 生產環境技術棧
建置和維護 Next.js 應用程式的偏好預設值。在 搭建新專案或擴充現有專案時,請遵循這些 慣例,除非使用者現有的程式碼庫明顯有不同的做法 — 當兩者衝突時,請比對現有模式而非此文件,並 簡短說明差異。
0. 技術棧一覽
| 層 | 推薦(偏好,非嚴格) | 若要求變更則提供的選項 |
|---|---|---|
| 框架 | Next.js (App Router), TypeScript, Server Components + Server Actions | 無需其他,這是核心 |
| 套件管理器 | pnpm | npm, yarn, bun |
| UI | Tailwind CSS, shadcn/ui, react-icons, Motion (motion.dev) | Radix UI 或 Mantine, lucide-react, CSS Modules |
| 表單 | React Hook Form + Zod | TanStack Form, Valibot 替代 Zod |
| 資料庫 | PostgreSQL + Drizzle ORM | Prisma, Kysely; 小型應用可使用 MySQL 或 SQLite |
| 驗證 | Better Auth, RBAC | Auth.js, Clerk, Supabase Auth |
| 媒體 | Cloudinary | UploadThing, S3 + CDN, Vercel Blob |
| 測試 | Vitest (單元/整合), Playwright (端對端) | Jest, Cypress |
| 監控 | Sentry, 結構化日誌 | Datadog, Highlight, Axiom + pino |
| 格式化/檢查 | ESLint, Prettier, TypeScript strict | Biome |
| CI/CD | GitHub + GitHub Actions, Vercel, preview deployments | GitLab CI, Netlify, Cloudflare Pages |
切勿在未告知使用者的情況下,從此表格中替換套件(例如以 Prisma 替代 Drizzle、以 next-auth 替代 Better Auth)。若使用者的專案已使用不同選擇,請繼續使用並標記此不一致,而非為同一工作混用兩種解決方案。
這些工具是「推薦」的:使用者偏好它們,但並非強制。
- 只要後續章節指名了特定工具(pnpm、Drizzle、Better Auth、 Cloudinary、Sentry 等),請將其視為此規則下的推薦預設值。
- 在變更、取代或跳過表格中的任何工具之前,請先詢問 使用者。提供選項:保留推薦工具,或使用最後一欄的替代方案 (或其他最佳實踐),並針對每個選項提供一行權衡取捨 以及您自己的建議。
- 在推薦替代方案之前,請檢查其當前狀態(維護狀況、重大變更)。 不要僅依賴記憶。
- 在獲得答案之前,絕不更換工具。在使用者回答之前,請保留 推薦的預設值。
1. 專案設定
- 使用
pnpm create next-app@latest搭建 — 啟用 TypeScript、App Router、Tailwind、ESLint,拒絕使用src/目錄,使app/保持在專案根目錄(僅限新專案)。 - 已使用
src/的現有專案: 保留src/—src/app/、src/components/、src/lib/、src/actions/。僅在專案沒有src/目錄時使用app/。請遵循現有的佈局和匯入別名(@/*→src/*);切勿在不相關的任務中將檔案移出src/。 - 對每個安裝/執行命令使用 pnpm(
pnpm add、pnpm dlx、pnpm dev)。切勿混用 npm/yarn 鎖定檔案。 - 透過
pnpm dlx shadcn@latest init初始化 shadcn/ui,然後按需新增元件(pnpm dlx shadcn@latest add button),而非預先安裝整個集合。 - 基準資料夾結構(無
src/—app/位於專案根目錄):
# 新專案(無 src/)
app/ # 路由、佈局、錯誤/載入/未找到邊界
components/ # 共享 UI(ui/ = shadcn 基礎元件)
lib/ # 資料庫、驗證、驗證規則、工具、速率限制、記錄器
actions/ # 按領域分組的伺服器操作
hooks/
types/
drizzle/ # 遷移
使用 src/ 的現有專案:在 src/ 下的結構相同(src/app/、src/components/、src/lib/、src/actions/)— 資料夾名稱保持不變,僅前綴變更。
2. TypeScript 與程式碼品質
- tsconfig 中設定
strict: true,無any(除非有註解說明理由),無@ts-ignore(除非有關聯的議題/原因)。 - 在程式碼被認為完成之前,需配置並通過 ESLint (next/core-web-vitals + typescript-eslint) 和 Prettier;實際執行它們,而非僅假設。
- 對匯出的函式和伺服器動作偏好明確的返回型別。
- 預設保持伺服器元件;僅在需要互動性、瀏覽器 API 或鉤子時才加入
"use client",並盡可能將邊界推至最低(最葉層級)。 - 每個檔案/模組一個職責;將路由的伺服器動作放在
actions/或路由旁邊的_actions.ts檔案中,而非內嵌在巨大的頁面檔案中。 - 架構慣例: 保持每種邏輯只有一個地方 — 資料存取在
lib/db、突變在actions/、共享 UI 在components/、跨域輔助程式在lib/。請遵循專案現有的命名慣例(檔案大小寫、元件命名),而非在過程中引入新慣例。 - 程式碼審查: 將每個非平凡的變更視為 PR 規模 — 清晰的差異、在請求審查前通過 lint/型別檢查/測試,以及說明變更內容和原因的請假。審查時(人工或 AI 輔助),請檢查是否缺少驗證檢查、未處理的錯誤以及慣例偏移,而不僅僅是檢查程式碼能否執行。
3. 渲染、資料與效能
- 靜態 vs 動態渲染: 預設靜態;僅在路由確實需要每個請求的資料時才選擇動態(
export const dynamic = 'force-dynamic'、cookies()、headers())。 - 資料擷取: 直接在伺服器元件中擷取;使用伺服器動作進行突變,除非消費者來自外部,否則不要使用客戶端擷取至 API 路由。
- 串流與 Suspense: 將慢速/可串流的區塊包裹在
<Suspense>中,並使用真實的骨架畫面(而非單純的旋轉動畫)作為 fallback,以便頁面其餘部分能立即渲染和串流 — 這也是loading.tsx在路由層級提供的功能。 - 快取與重新驗證: 對昂貴的讀取操作使用
use cache/unstable_cache(依 Next 版本而定);在突變後使用revalidatePath/revalidateTag精確失效,而非使用force-dynamic來「修正」過時問題。 - 預先擷取: 預先擷取使用者可能點擊的連結 —
<Link>會自動執行此操作;除非有特定理由,否則不要停用它(prefetch={false})。 - 延遲載入與程式碼分割: 對摺疊以下或少用的客戶端元件使用
next/dynamic進行延遲載入;避免來自僅在一個頁面上需要的函式庫的大型客戶端捆綁包。 - 圖片與字型最佳化: 始終對點陣圖使用
next/image(明確的width/height或fill+ 設定尺寸的容器),並對字型使用next/font— 絕不使用裸的<img>或<link>連結至 Google Fonts。 - Core Web Vitals: 將 LCP、CLS、INP 視為真實的限制條件 — 沒有導致佈局位移的後期內容、沒有未設定尺寸的媒體、沒有阻塞的同步腳本。
4. UI
- 使用 Tailwind 進行樣式設計;使用 shadcn/ui 元件作為基礎基礎元件,透過專案的主題 token(CSS 變數)進行自訂,而非分散在各檔案中的一次性內聯覆寫。
- 使用 react-icons 處理圖示(每個專案選擇一個圖示集,例如
react-icons/lu或/hi2,並保持一致)。 - 使用 Motion (motion.dev,前身為 Framer Motion) 處理動畫;保持動畫元件在客戶端且體積小,並尊重
prefers-reduced-motion。 - 預設響應式設計(行動優先的 Tailwind 斷點);不要提供僅限桌面的佈局。
5. 表單與驗證
- 為每個表單/資料形狀定義一個 Zod 結構;對客戶端驗證(透過
@hookform/resolvers/zod)和伺服器動作內部的伺服器端驗證重用相同的結構 — 絕不信任僅限客戶端的驗證。 - React Hook Form (
useForm) 驅動客戶端狀態;提交時,呼叫一個伺服器動作,從動作的結構化回傳值中顯示欄位錯誤(而非拋出導致通用錯誤的異常)。 - 在啟動時使用 Zod 結構驗證
process.env本身(一個lib/env.ts),以便缺失/格式錯誤的配置能在首次使用前快速失敗。
6. 資料庫 (PostgreSQL + Drizzle)
- 結構定義位於
lib/db/schema.ts(或按領域分割),遷移透過drizzle-kit generate產生並透過drizzle-kit migrate套用 — 絕不手動編輯產生的遷移檔案。 - 在無伺服器/邊緣環境中使用池化連線(例如透過驅動程式的池或 Neon/Supabase 池化提供者);不要在沒有池化的情況下每個請求都建立一個新客戶端。
- 查詢最佳化與 N+1 預防: 使用 Drizzle 的關聯查詢(
db.query.x.findMany({ with: {...} }))或明確的連接,而非迴圈查詢每列;僅選擇所需的欄位,而非擷取整列;對可能回傳無界結果集的任何查詢進行分頁/限制。 - 為
WHERE/ORDER BY/連接中使用的欄位建立索引;在結構定義中新增,而非事後補上。 - 為生產資料庫保留備份/還原文件的記錄(至少是受管提供者的快照)— 若使用者的設定沒有,請指出這一點。
7. 驗證與安全性
- 使用 Better Auth 進行驗證(工作階段、OAuth 提供者、必要時使用電子郵件/密碼);絕不自行實作密碼雜湊或工作階段 Token。
- RBAC:明確建模角色/權限(一個
role欄位加上一個小型權限檢查輔助函式),並在伺服器動作/資料存取層強制執行授權,而不僅僅是隱藏 UI — 隱藏的按鈕並非存取控制。 - 對敏感端點(驗證、表單提交、任何昂貴的操作)進行速率限制 — 例如透過 Upstash Redis 或等效的 Token 桶/滑動視窗限制器。
- 設定安全標頭(透過
next.config.jsheaders 或中介層):Content-Security-Policy、X-Frame-Options、Referrer-Policy、Permissions-Policy、Strict-Transport-Security。 - Cookie:在所有驗證/工作階段 Cookie 上設定
httpOnly、secure、sameSite: 'lax'(或在可行時設為'strict')。 - 在邊界處使用 Zod 驗證每個外部輸入(表單資料、查詢參數、Webhook 酬載),然後再觸及業務邏輯或資料庫。
- 絕不記錄機密、Token 或包含憑證的完整請求主體。
8. SEO
- 每個路由使用 Metadata API(
generateMetadata/ 靜態metadata匯出)— 標題、描述、Open Graph、Twitter Card。 - 提供
app/sitemap.ts和app/robots.ts,從真實路由資料產生,而非硬編碼的過時清單。 - 透過 metadata
alternates.canonical設定標準網址,特別是可透過多個查詢字串變體存取的路由。 - 為受益的內容類型(文章、產品、麵包屑、組織)使用帶有
type="application/ld+json"的 script 標籤新增 JSON-LD 結構化資料。
9. 無障礙
- 先使用語意化 HTML(
button、nav、main、header、label)— 僅在語意化 HTML 無法涵蓋的真實缺口時才使用 ARIA。 - 所有內容都能透過鍵盤單獨存取和操作;可見的焦點狀態(不要在不替換的情況下移除
:focus-visible外框)。 - 每張圖片都有有意義的
alt(僅對真正裝飾性的圖片使用空的alt="")。 - 每個表單輸入都有真正的關聯
<label>(不僅是佔位文字)。 - 至少檢查文字和互動元件的色彩對比度是否符合 WCAG AA 標準。
10. 錯誤處理與載入狀態
每個可能失敗或需要時間的路由區段都應具備:
error.tsx— 區段層級的錯誤邊界,帶有重試動作。global-error.tsx— 頂層的 fallback,用於error.tsx無法捕捉的錯誤。not-found.tsx— 用於 404 錯誤,包括來自資料查詢的notFound()呼叫。loading.tsx— Suspense fallback;製作一個與最終佈局相符的真實骨架畫面,而非單純的旋轉動畫。
11. 測試
- Vitest 用於單元測試(純函式、結構驗證、工具邏輯)和元件測試(React Testing Library)— 作為
*.test.ts(x)或放在__tests__/下。 - Playwright 用於端對端流程:驗證、關鍵使用者旅程、表單提交。針對預覽部署或本地建置執行,而非針對生產環境。
- 新的伺服器動作和驗證結構應至少附帶一個基本測試;不要讓關鍵業務邏輯在預設情況下未經測試。
12. 監控與記錄
- Sentry 用於客戶端和伺服器端的錯誤追蹤(檢測檔案 +
sentry.client.config/sentry.server.config),並在建置時上傳原始碼映射。 - 結構化記錄(JSON 日誌,包含請求 ID、路由、使用者 ID(如可用)),而非臨時的
console.log。 - 在生產環境中追蹤 Core Web Vitals(Vercel Analytics 或 Sentry Performance),並在部署的 URL 上設定正常執行時間監控。
13. DevOps
- Git 使用慣例性、範圍化的提交;受保護的
main分支。 - 每個 PR 上的 GitHub Actions CI:安裝 → lint → 型別檢查 → 單元測試 → 建置(針對較大變更加入端對端測試)。
- 部署到 Vercel;每個 PR 在合併前都會獲得一個預覽部署。
- 環境變數在 Vercel 中按環境管理,並在啟動時透過第 5 節的 Zod 環境結構進行驗證。
14. 媒體
- Cloudinary 用於使用者上傳/動態媒體:透過簽署的、伺服器核發的上傳參數上傳(絕不將 API 金鑰暴露在客戶端),並透過 Cloudinary 的轉換 URL 提供服務,而非重新實作調整大小。
15. Claude 的工作慣例
當被要求在使用此技術棧的專案中新增功能時:
- 在建立新的結構/動作/元件之前,先檢查現有的結構/動作/元件 — 擴充,而非重複。
- 新的資料庫欄位 → 更新
schema.ts,產生遷移,不要手動編輯 SQL。 - 新的表單 → Zod 結構 → RHF 表單 → 具有伺服器端重新驗證和授權檢查的伺服器動作。
- 新的路由 → 有意識地決定靜態 vs 動態;如果缺少,則新增
loading.tsx/error.tsx;新增 metadata。 - 在突變後,重新驗證受影響的特定路徑/標籤 — 不要全面重新驗證整個應用程式。
- 在呈現程式碼之前,在腦中對嚴格模式執行 lint/型別檢查;指出任何
any、未檢查的輸入或缺少的驗證檢查,而不是默默地交付。 - 在引入、取代或跳過第 0 節中的任何工具之前,向使用者提供選項(請參閱第 0 節中的「推薦,非嚴格」規則)進行詢問。
16. 附屬規則檔案
AGENTS.md— 專案工作流程規則(主流程、功能流程、核准閘道)