
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))時使用。
Official Feature-Sliced Design (FSD) v2.1 skill for applying the methodology to frontend projects. Use when the task involves organizing project structure with FSD layers, deciding where code belongs, placing static assets (images, icons, fonts, PDFs), grouping closely related slices, defining public APIs and import boundaries, resolving cross-imports or evaluating the @x pattern, deciding whether to create or remove an entity, evaluating whether the entities layer is needed at all, deciding whether logic should remain local or be extracted, migrating from FSD v2.0 or a non-FSD codebase, integrating FSD with frameworks (Next.js App Router and Pages Router, Nuxt, Vite, Astro), or implementing common patterns such as authentication, API handling, Redux, and TanStack Query (React Query) within FSD.
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,請避免同時載入此檔案。先處理結構,然後在後續步驟中(如有需要)再載入模式。





