Community程式設計與開發github.com

kartikk-k/skills

使用一致的 src 資料夾結構、領域控制器、Zod API 合約、組合式元件以及明確的客戶端/伺服器邊界來建構和維護 Next.js 網頁應用程式。適用於專案架構和應用程式編碼,同時尊重使用者選擇的函式庫和提供者。

skills 是什麼?

此技能提供了一套全面的 Next.js 網頁應用程式開發約定,強調使用「src/」目錄結構、基於 Zod 的 API 合約,以及將控制器與元件進行邏輯分組。它定義了明確的預設值(例如使用 Next.js App Router、TypeScript、Zod 驗證、TanStack Query 和 Zustand),但同時也允許使用者透過明確的指令來覆寫這些選項,或根據專案既有的設定進行調整。該技能強制執行嚴格的架構規則,包括使用控制器進行 API 呼叫、將 UI 邏輯與路由邏輯分離、使用組合式元件以及對用戶端與伺服器端程式碼進行嚴格的邊界管理。其目標是維持可擴展性、類型安全性以及簡潔的關注點分離,避免對不需要的領域(例如日誌記錄或資料庫)引入不必要的基礎設施。它支援多種平台,包括但不限於 Claude Code、Cursor、Codex 和 Windsurf,並根據 SKILL.md 文件的規範提供操作指導。

相容平台~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/kartikk-k/skills/tree/HEAD/skills/web-app-nextjs

Installed? Explore more 程式設計與開發 skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

在你喜歡的 AI 中提問

開啟一個已預先載入此 Agent Skill 的新對話。

說明文件

Next.js 網頁應用程式編碼慣例

將這些慣例應用於所要求的工作。使用者的明確指令優先於此技能中的任何規則或預設值。保留現有的產品需求與整合;應用此技能並不代表授權不相關的遷移或安裝每個可選的依賴項。

在實作之前,請檢查專案指示、已安裝的版本以及現有的慣例。當可用時,請閱讀相關已安裝的 Next.js 文件,特別是 node_modules/next/dist/docs/;否則請查閱與版本相符的官方文件。請勿假設來自其他版本的框架 API。

預設值與條件式功能

領域預設值覆寫或啟用規則
框架Next.js App Router 與嚴格的 TypeScript遵循專案選擇的版本與使用者的明確選擇。
元件使用 Tailwind CSS 的 Base UI,並包裝在本地元件中支援 Radix、shadcn/ui 或其他要求的函式庫。保留相同的元件組織方式。
變體class-variance-authority 以及一個支援 Tailwind 的 class 合併輔助函式重複使用已建立的等效工具。
API 傳輸在自訂的 APIClient 和 ServerAPIClient 抽象層之後使用 Axios現有的自訂客戶端、fetch 或其他要求的傳輸方式皆可。保持合約與傳輸方式無關。
驗證使用 Zod schema 與推斷出的 TypeScript 型別預設在外部資料邊界需要;僅在使用者明確指示時才更改。
遠端客戶端資料使用 TanStack Query 與一個共用的查詢包裝函式重複使用要求的等效工具。
共享互動狀態Zustand對於單一元件擁有的狀態,使用本地 React 狀態。尊重明確的替代方案。
驗證當要求驗證時,提供與提供者無關的電子郵件 OTP 流程遵循選擇的提供者與要求的登入方法;請勿隱含地選擇或佈建提供者。
跨頁籤同步為相關的共享突變事件啟用可根據要求選擇性啟用或移除。僅在跨頁籤一致性有用的地方加入事件。
日誌記錄與追蹤遵循現有的日誌系統如果不存在,除非被要求,否則請勿引入、提議或提及日誌/追蹤基礎設施。
模擬資料與模擬關閉僅在使用者要求模擬資料或開發/測試模擬時加入。
開發工具獨立的開發專用模組僅在相關或被要求時加入工具;沒有規定的工具或覆蓋層。

當需要做出決定時選擇預設值;不要詢問每個預設值。請勿將驗證、狀態儲存、同步或基礎設施引入不需要這些功能的特性中。

必要的資料夾結構

為新程式碼使用此組織方式。在責任變得必要時才建立目錄,而不是用空的佔位符填滿整個專案。在現有專案中,如果沒有遷移要求,請避免移動不相關的檔案。

src/
├── app/                    路由、佈局、API 端點
├── components/
│   ├── ui/                 共享 UI 基本元件
│   ├── standalone/         可重複使用的領域元件
│   ├── pages/<page>/       特定頁面的元件
│   └── <feature>/          功能子系統
├── controllers/
│   ├── client/<domain>/    瀏覽器 API 呼叫
│   └── server/<domain>/    伺服器端 API 呼叫
├── providers/              React Context 提供者
├── stores/                 Zustand 儲存
├── hooks/                  共享的 React Hook
├── helpers/                驗證/存取與其他工作流程
├── lib/                    API 客戶端、快取、日誌記錄、同步
├── db/                     資料庫/Redis 客戶端
├── utils/                  小型工具與常數
└── assets/icons/           來源 SVG

基礎設施的資料夾名稱描述的是放置位置,而非必要的服務。此樹狀結構不暗示任何資料庫、Redis、快取或日誌記錄堆疊。

  • 將領域程式碼組織在這些責任資料夾之下;避免用不同的頂層架構取代它們。
  • 使用 PascalCase 命名元件檔案,useSomething.ts 命名 Hook(僅在包含 JSX 時使用 .tsx),以及使用 camelCase 命名操作檔案,例如 updateName.ts。
  • 使用描述性的領域和功能目錄。保留框架檔案名稱不變,例如 page.tsx、layout.tsx 和 route.ts。
  • 使用 index.ts 來組合控制器命名空間,並使用 index.tsx 作為多檔案元件的入口點。
  • 使用 @/ 進行根目錄在 src 的匯入;對於緊密的內部同層級檔案,使用相對路徑匯入。對於僅型別依賴,請使用 import type。
  • 避免臨時副本、備份資料夾、重複實作以及責任不明的萬用檔案。

路由與元件組合

讓 app/ 專注於路由、佈局組合、請求處理和初始資料載入。在其佈局邊界有用時,使用路由群組,例如 (auth)、(no-auth) 和 (settings)。路由群組名稱本身不提供任何存取保護。

保持頁面精簡:載入初始資料、解決頁面層級的決策,並從 components/pages/<page>/ 或相關的功能目錄渲染元件。預設使用伺服器元件;為瀏覽器狀態、事件和 API 引入客戶端邊界。

元件放置:

  • components/ui/:與領域無關的基本元件以及所選 UI 函式庫的包裝器。
  • components/standalone/:跨頁面或功能使用的可重複使用領域控制元件。
  • components/pages/<page>/:特定於單一頁面的元件。
  • components/<feature>/:跨路由共享的大型子系統,例如導航或指令介面。

將複雜的元件拆分到專注的檔案中。 這是一項核心慣例。一個大型元件應該有一個目錄,其中包含一個 index.tsx 組合入口點,以及根據需要命名的支援元件、頁籤、行、Hook 和型別。請勿在一個檔案中累積多個大型子元件和不相關的副作用。根據責任而非任意的行數限制來拆分;避免將瑣碎的標記分割得過於零碎。

將互動機制保留在功能當地的 Hook 或專注的處理元件中:鍵盤導航、選擇、事件訂閱、拖拽行為和檢視協調。當一個 Hook 被共享時,將其移到 src/hooks/。保持輸入快捷鍵的範圍界定,以免干擾可編輯的控制項;在卸載時清理監聽器。

在本地包裝基本函式庫,並公開可組合的部分,例如 Dialog、DialogTrigger、DialogContent 和 DialogTitle。保留無障礙語意、焦點行為以及實際函式庫的組合 API。在未檢查相容性的情況下,請勿將 Radix 專有的 props 複製到 Base UI 包裝器中。

從底層基本元件或原生元素中定義包裝器 props 的型別,並僅加入需要的自訂 props。使用具型別的變體來處理大小、強調和狀態。以可預測的方式合併消費者的 class,並根據已安裝的 React/函式庫 API 保留 ref 支援。

使用連接到 Tailwind 工具的語意化 CSS 變數。保持主題值集中化,元件變體本地化。此技能規定的是樣式組織方式,而非視覺主題、間距尺度或產品設計。

在 app/providers.tsx 中組合相關的 Context 提供者;將其實作保留在 providers/ 中。將僅限瀏覽器的提供者放在明確的客戶端邊界之後,並避免將開發工具初始化與正常的提供者責任混在一起。

控制器與 HTTP 客戶端

在 controllers/client/<domain>/ 或 controllers/server/<domain>/ 下,每個檔案一個操作。透過 index.ts 檔案組裝巢狀的命名空間物件,例如:

ClientController.org.members.list()
ClientController.profile.updateName(input)
ServerController.org.info()

控制器提供具型別的操作和端點建構。元件應呼叫控制器,而不是組合 URL 或傳輸配置。控制器保持精簡;將可重複使用的工作流程邏輯放在 helpers/ 中,將基礎設施放在 lib/ 中。

保持瀏覽器和伺服器 HTTP 客戶端分離:

  • lib/APIClient.ts:瀏覽器安全的傳輸、查詢/主體序列化、回應處理和取消支援。
  • lib/ServerAPIClient.ts:僅限伺服器的傳輸、受信任的基礎 URL 以及請求範圍的憑證/工作階段上下文。
  • 將伺服器機密和僅限伺服器的依賴項保留在瀏覽器匯入之外。切勿將每位使用者的憑證保存在共享的可變單例中。
  • 當伺服器控制器呼叫應用程式端點時,僅將所需的憑證轉發到受信任的目的地。請勿信任呼叫者提供的身份標頭作為驗證。
  • 一致地標準化傳輸錯誤、非 2xx 回應和應用程式錯誤。保留有意義的狀態/程式碼資訊。請勿將失敗的請求變成成功的空資料。
  • 透過控制器和傳輸層支援 AbortSignal。取消不應觸發錯誤通知或重試。

API 結構、合約與處理器

Zod schema 是輸入的預設真實來源。為主體、查詢和路徑參數定義特定的 schema,包括格式、允許的值、長度/範圍限制、可選性和故意的強制轉換。在使用外部值之前進行驗證。避免使用 z.any()、寬泛的記錄、型別斷言和非空斷言來代替合約。為每個邊界審慎處理未知鍵。

預設情況下,將特定於方法的路由 schema 和匯出的請求/查詢/回應型別與路由放在一起。使用 z.infer 推斷請求型別;定義明確的回應合約,並在需要執行時期驗證的地方使用回應 schema。在控制器和元件中重複使用這些型別,而不是重新宣告它們。如果執行時期合約必須與瀏覽器程式碼共享,或者路由模組匯出約束要求這樣做,請使用相鄰的 schema.ts 或 types.ts 以及僅型別匯入,這樣伺服器實作永遠不會被拉入客戶端套件。

使用一致的可區分回應信封:

type APIResponse<T> =
  | { success: true; data: T; error: null }
  | {
      success: false;
      data: null;
      error: { code: string; message: string; details?: unknown };
    };

在使用時,將 details 縮小到明確的安全驗證/錯誤詳細資訊型別。保持所有分支的錯誤和 HTTP 狀態一致。請勿在正式環境回應中揭露內部異常或堆疊追蹤。

將多方法的路由組織成特定於方法的 schema/型別區段和函式,例如 get_request 和 post_request。透過 lib/api/APIRequestHandler.ts 中的 withAPIRequestHandler() 匯出處理器。

維護 lib/api/error/,其中包含一個 APIError 類別和一個按領域組織、具型別的錯誤代碼、訊息和狀態目錄。共享處理器將驗證失敗、領域錯誤和意外失敗映射到相同的回應合約。僅在上述日誌記錄條件下整合日誌記錄。

將重複的組織查詢、身分識別解析、查詢解析和存取工作流程提取到 helpers/ 中。將小型純轉換和常數保留在 utils/ 中。將較大的整合拆分成專注的工作流程檔案;路由應協調這些步驟,而不是包含整個整合。

資料擷取與狀態

在適當時透過伺服器控制器載入初始頁面資料,並將具型別、可序列化的 props 傳入互動式元件。對獨立的請求使用 Promise.all;為依賴性操作保留順序。

對遠端客戶端資料使用 TanStack Query。提供共用的預設值和一個 useApiQuery 包裝函式,該函式解開 API 信封、傳播錯誤並轉發查詢取消訊號。查詢鍵必須包含相關的身分識別/資源/篩選器輸入。將伺服器端的查詢客戶端範圍限定於每個請求;在突變後失效或更新受影響的查詢條目。

對共享的互動式狀態(例如選擇、篩選器、面板和命令狀態)使用特定於功能的 Zustand 儲存。訂閱個別的切片或使用淺層選擇來處理分組值。保持 setter/action 明確,並在其擁有的資源變更時重置狀態。避免在 Zustand 中維護第二個獨立的遠端資料副本,因為 Query 已經擁有它。

對元件擁有的輸入、髒狀態、驗證訊息和儲存狀態使用本地狀態。防止重複提交並在使用者輸入失敗時保留它。沒有必要的形式函式庫。

取消被取代的搜尋/篩選請求,並在清理時中止。防止過時的完成結果更新新資源。僅將實際的取消歸類為取消;請勿默默地吞掉網路失敗。

對於大型列表,在有用時應用虛擬化和分頁,並包含載入中、空、錯誤、重試和列表結尾狀態。在資源/篩選器變更時適當地重置分頁和選擇。保持鍵盤導航與虛擬化行一致。

可選的跨頁籤同步

預設為相關的共享突變啟用同步,除非使用者停用它。請勿將其加入沒有同步需求的孤立狀態。

在 lib/local-sync/ 中使用一個具型別的事件註冊表,在 events/ 下使用領域事件 schema,以及一個共享的 hooks/useLocalSync.ts Hook。從 Zod 推斷載荷型別,並驗證傳出和傳入的訊息。

一個儲存事件實作應通知其他頁籤和同頁籤的監聽器,使用短暫的事件載荷而不是持久化的應用程式狀態,並清理監聽器。現有的等效傳輸方式是可接受的。防止回音迴圈和重複處理;請勿同步機密資訊。事件應更新或失效規範的狀態擁有者。跨頁籤事件是 UI 協調,而非授權或保證的傳遞。

驗證與授權

當驗證在範圍內時套用此區段。保持實作與提供者無關,並適應所選的服務或自訂後端。

  • 對於電子郵件 OTP 驗證,分離傳送驗證碼、驗證驗證碼和登出處理程序...

(文件已截斷以供翻譯)

相關技能