
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. 对实体保持保守
实体层高度可访问(几乎所有其他层都可以从中导入),因此变更传播广泛。
- 从没有实体开始。
shared/+pages/+app/是有效的 FSD。瘦客户端应用很少需要实体。 - 不要过早拆分切片。 将代码保留在页面中。仅当相同代码当前被多个消费者使用且边界稳定时,才提取到实体。
- 业务逻辑并不自动需要实体。 将类型保留在
shared/api中,逻辑保留在当前切片的model/段中可能就足够了。 - 将 CRUD 放在
shared/api/中。 CRUD 是基础设施,不是实体。 - 将认证数据放在
shared/auth/或shared/api/中。 令牌和登录 DTO 依赖于认证上下文,很少在认证之外复用。
关于保持实体层清洁的详细指导(何时完全跳过它、如何隔离业务上下文、为什么 CRUD 属于 shared/api),请参见 references/excessive-entities.md。
5-3. 从最小层开始
// ✅ 有效的最小 FSD 项目
src/
app/ ← 提供者、路由
pages/ ← 所有页面级代码
shared/ ← UI 套件、工具函数、API 客户端
// 仅当实际用例需要时才添加层:
// + widgets/ ← 当前跨多个页面复用的 UI 块
// + features/ ← 当前跨多个页面复用的用户交互
// + entities/ ← 当前跨页面或功能复用的领域模型
5-4. 使用 Steiger 检查器验证
Steiger 是官方 FSD 检查器。关键规则:
insignificant-slice:建议将仅被一个页面使用的实体/功能合并到该页面。excessive-slicing:建议在层中切片过多时进行合并或分组。
npm install -D @feature-sliced/steiger
npx steiger src
6. 反模式(避免)
- 不要过早创建实体。 仅在一个地方使用的数据结构应属于该地方。
- 不要将 CRUD 放在实体中。 使用
shared/api/。仅对复杂的事务逻辑考虑实体。 - 不要仅为认证数据创建
user实体。 令牌和登录 DTO 属于shared/auth/或shared/api/。 - 不要滥用
@x。 它是必要的妥协,而非推荐模式。该符号仅用于实体层,且仅在边界合并确实不可能时使用。功能和部件通过策略 A–D 处理交叉导入(见第 7 节)。 - 不要提取单次使用的代码。 仅被一个页面使用的功能或实体应保留在该页面中。
- 不要使用技术角色文件名。 使用基于领域的名称(见规则 4-4)。
- 向实体添加 UI 时要谨慎。 实体 UI 会诱使其他实体进行交叉导入。如果向实体添加 UI 段,只能从更高层(功能、部件、页面)导入,绝不能从其他实体导入。
- 不要创建上帝切片。 职责过于宽泛的切片应拆分为聚焦的切片(例如,将
user-management/拆分为auth/、profile-edit/、password-reset/)。 - 不要创建顶层
assets/段。 将静态资源放在使用它们的代码旁边。参见references/asset-handling.md。
7. 交叉导入解决
交叉导入是代码异味,而非绝对禁止。正确的策略取决于层和情况。
实体层:优先合并边界,@x 是最后手段
实体中的交叉导入通常是由于实体拆分过于细粒度。在考虑 @x 之前,先考虑是否应合并边界。
@x 是必要的妥协,而非推荐方法。仅在边界确实无法合并时使用,并记录原因。过度使用会将实体边界锁定在一起,增加重构成本。
功能和部件:四种策略(A、B、C、D)
在 features 和 widgets 中,根据上下文选择:
- 策略 A:切片合并。 两个切片总是一起变化 → 合并。
- 策略 B:推送到实体。 共享领域逻辑 → 移动到
entities/,将 UI 保留在功能/部件中。 - 策略 C:从上层组合(IoC)。 父级(页面或应用)导入两个切片并通过渲染属性、插槽或 DI 连接它们。
- 策略 D:公共 API 访问。 当复用确实不可避免时,仅通过切片的
index.ts允许。绝不要深入到model/、store/或内部文件。
@x 符号仅用于实体层。功能和部件使用上述策略 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/ - 何时创建实体:当相同的业务领域模型当前跨多个页面、功能或部件使用,且边界稳定时。
- 何时创建功能:当相同的用户交互当前跨多个页面或部件使用,且边界稳定时。
- 打破规则:仅作为有意的设计选择。在代码中记录原因(注释或 ADR)。
- 交叉导入解决(实体):首先合并边界;
@x是必要的妥协,不推荐。 - 交叉导入解决(功能/部件):策略 A(合并)、B(推送到实体)、C(从上层组合)或 D(公共 API)。
@x符号仅用于实体。 - 文件命名:基于领域(
user.ts、order.ts)。绝不要使用技术角色(types.ts、utils.ts)。 - 资源放置:放在使用它们的代码旁边;复用内容放入
shared/ui/;全局样式表和字体放入app/。 - 切片组:大型层的可选导航辅助;组文件夹没有段和公共 API。
- Processes 层:已弃用。参见
references/migration-guide.md。
11. 条件参考
仅在特定情况适用时阅读以下参考文件。不要预加载所有参考。
-
当创建、审查或重组 FSD 层和切片的文件夹和文件结构,包括将紧密相关的切片分组到父文件夹以进行导航(例如,“设置项目结构”、“此文件夹放在哪里”、“如何对这些支付实体进行分组”):
→ 阅读references/layer-structure.md -
当解决同层切片之间的交叉导入问题、评估
@x模式、为功能和部件选择策略 A/B/C/D,或决定是否应合并边界时:
→ 阅读references/cross-import-patterns.md -
当决定是否创建或移除实体、处理过多实体、评估是否完全跳过实体层、放置 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 -
当在 FSD 结构内实现具体代码模式,如认证、API 请求处理、类型定义或状态管理(Redux、TanStack Query / React Query,包括查询工厂、无限滚动、Suspense 模式和
useMutationState)时:
→ 阅读references/practical-examples.md
注意:如果在此对话中已加载layer-structure.md,请避免同时加载此文件。先处理结构,然后在后续步骤中根据需要加载模式。



