feature-sliced-design

feature-sliced-design

官方 Feature-Sliced Design (FSD) v2.1 技能,用於將此方法論應用於前端專案。當任務涉及使用 FSD 層組織專案結構、決定程式碼歸屬、放置靜態資源(圖片、圖示、字型、PDF)、分組緊密相關的切片、定義公開 API 和匯入邊界、解決跨匯入或評估 @x 模式、決定是否建立或移除實體、評估是否需要 entities 層、決定邏輯應保留在本地還是提取、從 FSD v2.0 或非 FSD 程式碼庫遷移、將 FSD 與框架(Next.js App Router 和 Pages Router、Nuxt、Vite、Astro)整合,或在 FSD 內實作常見模式(如驗證、API 處理、Redux 和 TanStack Query (React Query))時使用。

65星標
0分支
更新於 2026/7/11
SKILL.md
唯讀
名稱
feature-sliced-design
描述

官方 Feature-Sliced Design (FSD) v2.1 技能,用於將此方法論應用於前端專案。當任務涉及使用 FSD 層組織專案結構、決定程式碼歸屬、放置靜態資源(圖片、圖示、字型、PDF)、分組緊密相關的切片、定義公開 API 和匯入邊界、解決跨匯入或評估 @x 模式、決定是否建立或移除實體、評估是否需要 entities 層、決定邏輯應保留在本地還是提取、從 FSD v2.0 或非 FSD 程式碼庫遷移、將 FSD 與框架(Next.js App Router 和 Pages Router、Nuxt、Vite、Astro)整合,或在 FSD 內實作常見模式(如驗證、API 處理、Redux 和 TanStack Query (React Query))時使用。

Feature-Sliced Design (FSD) v2.1

來源: fsd.how | 嚴格程度可根據專案規模和團隊情境調整。


1. 核心哲學與層級概覽

FSD v2.1 核心原則:「從簡單開始,需要時再提取。」

先將程式碼放在 pages/ 中。跨頁面的重複是可接受的,不自動要求提取到較低層級。僅在相同程式碼目前確實被多處使用(非假設性)、這些使用情境不會總是同時變動、且邊界具有明確職責時,才進行提取。

並非所有層級都是必需的。 大多數專案只需 shared/pages/app/ 即可開始。僅在 widgets/features/entities/ 能提供明確價值時才加入。不要「以防萬一」建立空的層級資料夾。

FSD 使用 6 個標準化層級,從高到低排列如下:

app/       → 應用程式初始化、提供者、路由
pages/     → 路由層級的組合,擁有自己的邏輯
widgets/   → 跨多個頁面重複使用的大型複合 UI 區塊
features/  → 可重複使用的使用者互動(僅在 2 個以上地方使用時)
entities/  → 可重複使用的業務領域模型(僅在 2 個以上地方使用時)
shared/    → 不含業務邏輯的基礎設施(UI 套件、工具函式、API 客戶端)

匯入規則:模組只能從嚴格低於它的層級匯入。同一層級內切片之間的跨匯入是被禁止的。

// ✅ 允許
import { Button } from "@/shared/ui/Button"; // features → shared
import { useUser } from "@/entities/user"; // pages → entities

// ❌ 違規
import { loginUser } from "@/features/auth"; // entities → features
import { likePost } from "@/features/like-post"; // features → features

注意processes/ 層級在 v2.1 中已棄用。遷移細節請參閱 references/migration-guide.md


2. 決策框架

撰寫新程式碼時,請遵循以下決策樹:

步驟 1:這段程式碼用在何處?

  • 僅用於一個頁面 → 保留在該 pages/ 切片中。
  • 用於 2 個以上頁面,但重複是可管理的 → 在每個頁面中保留獨立副本也是有效的做法。
  • 一個實體或功能僅用於一個頁面 → 保留在該頁面中(Steiger:insignificant-slice)。

步驟 2:它是否為不含業務邏輯的可重複使用基礎設施?

  • UI 元件 → shared/ui/
  • 工具函式 → shared/lib/
  • API 客戶端、路由常數 → shared/api/shared/config/
  • 驗證令牌、工作階段管理 → shared/auth/
  • CRUD 操作 → shared/api/

步驟 3:它是否為目前在多處使用、且邊界穩定的完整使用者操作?

  • 是 → features/
  • 不確定、僅單一使用或推測性重複使用 → 保留在頁面中。

步驟 4:它是否為目前在多處使用、且邊界穩定的業務領域模型?

  • 是 → entities/
  • 不確定、僅單一使用或推測性重複使用 → 保留在頁面中。

步驟 5:它是否為應用程式層級的設定?

  • 全域提供者、路由器、主題 → app/

黃金法則:有疑問時,保留在 pages/ 中。僅在相同程式碼確實被多處使用且邊界明確時才進行提取。


3. 快速放置表

情境 單一使用 確認多處使用
使用者個人資料表單 pages/profile/ui/ProfileForm.tsx features/profile-form/
產品卡片 pages/products/ui/ProductCard.tsx entities/product/ui/ProductCard.tsx
產品資料擷取 pages/product-detail/api/fetch-product.ts entities/product/api/
驗證令牌/工作階段 shared/auth/(總是) shared/auth/(總是)
驗證登入表單 pages/login/ui/LoginForm.tsx features/auth/
CRUD 操作 shared/api/(總是) shared/api/(總是)
通用卡片佈局 shared/ui/Card/
模態視窗管理器 shared/ui/modal-manager/
模態視窗內容 pages/[page]/ui/SomeModal.tsx
日期格式化工具 shared/lib/format-date.ts

4. 架構規則(必須遵守)

這些規則是 FSD 的基礎。違反規則會削弱架構。如果必須打破規則,請確保這是有意的設計決策,並在程式碼中記錄原因(註解或 ADR)。

4-1. 僅從較低層級匯入

app → pages → widgets → features → entities → shared
禁止向上匯入以及同一層級內切片之間的跨匯入。

4-2. 公開 API:每個切片透過 index.ts 匯出

外部消費者只能從切片的 index.ts 匯入。禁止直接匯入內部檔案。

// ✅ 正確
import { LoginForm } from "@/features/auth";

// ❌ 違規:繞過公開 API
import { LoginForm } from "@/features/auth/ui/LoginForm";

Shared 層級:Shared 沒有切片。為每個區段定義獨立的公開 API(shared/ui/index.tsshared/api/index.ts 等),而不是單一頂層的 shared/index.ts。這有助於根據意圖組織來自 Shared 的匯入。

環境特定的公開 API

切片通常應透過單一 index.ts 公開其公開 API。不建議臨時自訂。

如果單一 index.ts 無法維持執行時期邊界,請新增環境特定的進入點,例如 index.server.ts。請參閱 references/framework-integration.md

4-3. 同一層級內切片之間禁止跨匯入

如果同一層級的兩個切片需要共享邏輯,請遵循第 7 節的解決順序。不要建立直接匯入。

4-4. 基於領域的檔案命名(禁止去區段化)

根據檔案所代表的業務領域命名,而非其技術角色。技術角色名稱如 types.tsutils.tshelpers.ts 會將不相關的領域混合在單一檔案中,降低內聚力。

// ❌ 技術角色命名
model/types.ts          ← 哪些型別?使用者?訂單?混合?
model/utils.ts

// ✅ 基於領域命名
model/user.ts           ← 使用者型別 + 相關邏輯
model/order.ts          ← 訂單型別 + 相關邏輯
api/fetch-profile.ts    ← 明確目的

4-5. shared/ 中不得包含業務邏輯

Shared 僅包含基礎設施:UI 套件、工具函式、API 客戶端設定、路由常數、資源。業務計算、領域規則和工作流程應屬於 entities/ 或更高層級。

// ❌ 業務邏輯在 shared 中
// shared/lib/userHelpers.ts
export const calculateUserReputation = (user) => { ... };

// ✅ 移至所屬領域
// entities/user/lib/reputation.ts
export const calculateUserReputation = (user) => { ... };

5. 建議(應該遵守)

5-1. 頁面優先:將程式碼放在使用處

先將程式碼放在 pages/ 中。僅在真正需要時才提取到較低層級。提取是影響整個專案的設計決策,因此門檻應較高。

保留在頁面中的內容:

  • 僅用於一個頁面的大型 UI 區塊
  • 頁面特定的表單、驗證、資料擷取、狀態管理
  • 頁面特定的業務邏輯和 API 整合
  • 看似可重複使用但保留在本地更簡單的程式碼

演化模式:先將所有內容放在 pages/profile/ 中。當相同使用者資料被另一個頁面使用時(非假設性),將共享模型提取到 entities/user/。將頁面特定的 API 呼叫和 UI 保留在頁面中。

5-2. 對 entities 保持保守

entities 層級具有高度可存取性(幾乎所有其他層級都可以從中匯入),因此變更會廣泛傳播。

  1. 從沒有 entities 開始。 shared/ + pages/ + app/ 是有效的 FSD。瘦客戶端應用程式很少需要 entities。
  2. 不要過早拆分切片。 將程式碼保留在頁面中。僅在相同程式碼目前被多個消費者使用且邊界穩定時,才提取到 entities。
  3. 業務邏輯不自動需要實體。 將型別保留在 shared/api 中,邏輯保留在目前切片的 model/ 區段中可能就足夠了。
  4. 將 CRUD 放在 shared/api/ 中。 CRUD 是基礎設施,不是 entities。
  5. 將驗證資料放在 shared/auth/shared/api/ 中。 令牌和登入 DTO 依賴於驗證上下文,很少在驗證之外重複使用。

關於保持 entities 層級整潔的詳細指南(何時完全跳過它、如何隔離業務上下文、為何 CRUD 屬於 shared/api),請參閱 references/excessive-entities.md

5-3. 從最少層級開始

// ✅ 有效的最小 FSD 專案
src/
  app/         ← 提供者、路由
  pages/       ← 所有頁面層級程式碼
  shared/      ← UI 套件、工具函式、API 客戶端

// 僅在實際使用案例需要時才加入層級:
// + widgets/   ← 目前跨多個頁面重複使用的 UI 區塊
// + features/  ← 目前跨多個頁面重複使用的使用者互動
// + entities/  ← 目前跨頁面或功能重複使用的領域模型

5-4. 使用 Steiger linter 驗證

Steiger 是官方 FSD linter。關鍵規則:

  • insignificant-slice:如果某個實體/功能僅被一個頁面使用,建議將其合併到該頁面中。
  • excessive-slicing:當某個層級有太多切片時,建議合併或分組。
npm install -D @feature-sliced/steiger
npx steiger src

6. 反模式(避免)

  • 不要過早建立 entities。 僅在一個地方使用的資料結構應屬於該處。
  • 不要將 CRUD 放在 entities 中。 使用 shared/api/。僅在複雜的交易邏輯中考慮 entities。
  • 不要僅為了驗證資料而建立 user 實體。 令牌和登入 DTO 屬於 shared/auth/shared/api/
  • 不要濫用 @x 這是一個必要的妥協,而非推薦的模式。此標記僅適用於 entities 層級,且僅在邊界合併確實不可能時使用。Features 和 widgets 透過策略 A–D 處理跨匯入(請參閱第 7 節)。
  • 不要提取單一使用的程式碼。 僅被一個頁面使用的功能或實體應保留在該頁面中。
  • 不要使用技術角色檔案名稱。 使用基於領域的名稱(請參閱規則 4-4)。
  • 謹慎將 UI 加入 entities。 Entity UI 容易引發來自其他 entities 的跨匯入。如果將 UI 區段加入 entities,只能從較高層級(features、widgets、pages)匯入它們,絕不能從其他 entities 匯入。
  • 不要建立上帝切片。 職責過於廣泛的切片應拆分為專注的切片(例如,將 user-management/ 拆分為 auth/profile-edit/password-reset/)。
  • 不要建立頂層的 assets/ 區段。 將靜態資源放在使用它們的程式碼旁邊。請參閱 references/asset-handling.md

7. 跨匯入解決方案

跨匯入是一種程式碼壞味道,而非絕對禁止。正確的策略取決於層級和情境。

Entities 層級:優先合併邊界,@x 是最後手段

Entities 中的跨匯入通常是由於切片拆分得過於細粒度。在考慮 @x 之前,先評估邊界是否應該合併。

@x必要的妥協,而非推薦的方法。僅在邊界確實無法合併時使用,並記錄原因。過度使用會將 entity 邊界鎖在一起,增加重構成本。

Features 和 widgets:四種策略(A、B、C、D)

featureswidgets 中,根據情境選擇:

  • 策略 A:切片合併。 兩個切片總是同時變動 → 合併。
  • 策略 B:推送到 entities。 共享的領域邏輯 → 移至 entities/,將 UI 保留在 features/widgets 中。
  • 策略 C:從上層組合(IoC)。 父層(pages 或 app)匯入兩個切片並透過 render props、slots 或 DI 連接它們。
  • 策略 D:公開 API 存取。 當重複使用確實無法避免時,僅允許透過切片的 index.ts 進行。絕不能深入 model/store/ 或內部檔案。

@x 標記僅適用於 entities 層級。Features 和 widgets 使用上述策略 A–D。

嚴格程度取決於專案情境

跨匯入通常是最好避免的依賴關係,但有時會有意使用。嚴格程度因專案情境而異:

  • 早期產品且大量實驗:允許某些跨匯入可能是務實的速度權衡。
  • 長期或受監管的系統(金融科技、大規模服務):更嚴格的邊界有助於可維護性和穩定性。

如果引入跨匯入,請將其視為有意的選擇,並在程式碼中記錄理由(解釋為何其他策略不適用的註解)。

關於每種策略的詳細程式碼範例,請參閱 references/cross-import-patterns.md


8. 區段與結構規則

標準區段

區段根據技術目的對切片內的程式碼進行分組:

  • ui/:UI 元件、樣式、顯示相關程式碼
  • model/:資料模型、狀態儲存、業務邏輯、驗證
  • api/:後端整合、請求函式、API 特定型別
  • lib/:此切片的內部工具函式
  • config/:設定、功能開關

層級結構規則

  • App 和 Shared:沒有切片,直接按區段組織。這些層級內的區段可以互相匯入。
  • Pages、Widgets、Features、Entities:先有切片,然後每個切片內有區段。
  • 切片群組(可選):群組資料夾可以包含同一層級上的相關切片,僅用於導航目的。群組沒有區段,也沒有公開 API。詳情請參閱 references/layer-structure.md

區段內的檔案命名

始終使用基於領域的名稱,描述程式碼的內容:

model/user.ts            ← 使用者型別 + 邏輯 + 儲存
model/order.ts           ← 訂單型別 + 邏輯 + 儲存
api/fetch-profile.ts     ← 個人資料擷取
api/update-settings.ts   ← 設定更新

如果某個區段只有一個領域關注點,檔案名稱可以與切片名稱相同(例如 features/auth/model/auth.ts)。


9. Shared 層級指南

Shared 包含不含業務邏輯的基礎設施。它僅按區段組織(沒有切片)。Shared 內的區段可以互相匯入。

允許在 shared 中的內容:

  • ui/:UI 套件(Button、Input、Modal、Card)
  • lib/:工具函式(formatDate、debounce、classnames)
  • api/:API 客戶端、路由常數、CRUD 輔助函式、基礎型別
  • auth/:驗證令牌、登入工具函式、工作階段管理
  • config/:環境變數、應用程式設定
  • assets/:跨應用程式共享的品牌資源(謹慎使用;請參閱 references/asset-handling.md

Shared 可以包含應用程式感知的程式碼(路由常數、API 端點、品牌資源、通用型別)。它絕不能包含業務邏輯、功能特定程式碼或實體特定程式碼。


10. 快速參考

  • 匯入方向app → pages → widgets → features → entities → shared
  • 最小 FSDapp/ + pages/ + shared/
  • 建立 entities 時機:當相同的業務領域模型目前跨多個頁面、功能或 widgets 使用,且邊界穩定時。
  • 建立 features 時機:當相同的使用者互動目前跨多個頁面或 widgets 使用,且邊界穩定時。
  • 打破規則:僅作為有意的設計選擇。在程式碼中記錄原因(註解或 ADR)。
  • 跨匯入解決方案(entities):先合併邊界;@x 是必要的妥協,不推薦。
  • 跨匯入解決方案(features/widgets):策略 A(合併)、B(推送到 entities)、C(從上層組合)或 D(公開 API)。@x 標記僅適用於 entities。
  • 檔案命名:基於領域(user.tsorder.ts)。絕不使用技術角色名稱(types.tsutils.ts)。
  • 資源放置:放在使用它們的程式碼旁邊;重複使用的資源放到 shared/ui/;全域樣式表和字型放到 app/
  • 切片群組:可選的導航輔助工具,適用於大型層級;群組資料夾沒有區段和公開 API。
  • Processes 層級:已棄用。請參閱 references/migration-guide.md

11. 條件式參考文件

僅在特定情況適用時閱讀以下參考檔案。不要預先載入所有參考文件。

  • 當建立、審查或重組 FSD 層級和切片的資料夾與檔案結構,包括將緊密相關的切片分組到父資料夾以利導航(例如「設定專案結構」、「這個資料夾該放哪裡」、「如何分組這些付款實體」):
    → 閱讀 references/layer-structure.md

  • 當解決同一層級內切片之間的跨匯入問題、評估 @x 模式、為 features 和 widgets 選擇策略 A/B/C/D,或決定邊界是否應合併:
    → 閱讀 references/cross-import-patterns.md

  • 當決定是否建立或移除實體、處理過多實體、評估是否完全跳過 entities 層級、放置 CRUD 操作、決定驗證資料的歸屬,或隔離業務上下文以避免 @x 鏈:
    → 閱讀 references/excessive-entities.md

  • 當決定靜態資源的放置位置(圖片、圖示、字型、PDF、樣式表)用於單一切片、跨切片共享或全域使用:
    → 閱讀 references/asset-handling.md

  • 當從 FSD v2.0 遷移到 v2.1、將非 FSD 程式碼庫轉換為 FSD,或棄用 processes 層級:
    → 閱讀 references/migration-guide.md

  • 當將 FSD 與特定框架整合(Next.js 搭配 App Router 或 Pages Router、Nuxt、Vite、CRA、Astro),用於將路由連接到 FSD 頁面、放置中介軟體/儀器檔案、建構 API 路由處理程式,或設定路徑別名:
    → 閱讀 references/framework-integration.md

  • 當實作具體的程式碼模式,用於驗證、API 請求處理、型別定義或狀態管理(Redux、TanStack Query / React Query,包括查詢工廠、無限捲動、Suspense 模式和 useMutationState)在 FSD 結構內:
    → 閱讀 references/practical-examples.md
    注意:如果您在此對話中已經載入 layer-structure.md,請避免同時載入此檔案。先處理結構,然後在後續步驟中(如有需要)再載入模式。