
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 層組織專案結構、決定程式碼歸屬、放置靜態資源(圖片、圖示、字型、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.ts、shared/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.ts、utils.ts、helpers.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 層級具有高度可存取性(幾乎所有其他層級都可以從中匯入),因此變更會廣泛傳播。
- 從沒有 entities 開始。
shared/+pages/+app/是有效的 FSD。瘦客戶端應用程式很少需要 entities。 - 不要過早拆分切片。 將程式碼保留在頁面中。僅在相同程式碼目前被多個消費者使用且邊界穩定時,才提取到 entities。
- 業務邏輯不自動需要實體。 將型別保留在
shared/api中,邏輯保留在目前切片的model/區段中可能就足夠了。 - 將 CRUD 放在
shared/api/中。 CRUD 是基礎設施,不是 entities。 - 將驗證資料放在
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)
在 features 和 widgets 中,根據情境選擇:
- 策略 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 - 最小 FSD:
app/+pages/+shared/ - 建立 entities 時機:當相同的業務領域模型目前跨多個頁面、功能或 widgets 使用,且邊界穩定時。
- 建立 features 時機:當相同的使用者互動目前跨多個頁面或 widgets 使用,且邊界穩定時。
- 打破規則:僅作為有意的設計選擇。在程式碼中記錄原因(註解或 ADR)。
- 跨匯入解決方案(entities):先合併邊界;
@x是必要的妥協,不推薦。 - 跨匯入解決方案(features/widgets):策略 A(合併)、B(推送到 entities)、C(從上層組合)或 D(公開 API)。
@x標記僅適用於 entities。 - 檔案命名:基於領域(
user.ts、order.ts)。絕不使用技術角色名稱(types.ts、utils.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,請避免同時載入此檔案。先處理結構,然後在後續步驟中(如有需要)再載入模式。



