Feature Sliced 架构设计

Published on 22 April 2024
12 min read
React
Feature Sliced 架构设计

什么是 Feature Sliced?

FSD - Feature Sliced - (功能切片设计) 是一种用于构建前端应用程序的架构方法。可以将其理解为前端项目结构、代码约定和规则的规范。它的目的是让项目在应对不断变化的业务需求时,更加易于理解和组织,便于扩展和维护。

是否适合你的项目?

  • 这种架构方法论只适用于前端项目,不适用与后端
  • 仅适用于面向用户的应用程序,而不适用与库或者 UI 工具包
  • 小项目可能不需要 FSD,并且可能会额外提高项目的复杂性
  • 特别复杂的项目,也依然可以应用 FSD 的架构模式
无论你是哪种项目,是否适合,都可以了解下 FSD,从其中或者灵感 💡

分层结构

CleanShot 2024-05-29 at 13.54.51@2x.png

在 FSD 架构中,可以分为从下到下三层,分别为 LayersSlices 以及 SegmentsLayers 是顶层目录,它最多六层 (之前是七层),并且是标准化的,但是其中一些是可选的。

在 FSD 中,一个项目由 Layers 组成,LayersSlices 组层, SlicesSegments 组成。

Layers

  • app: 应用程序开始的地方,这里定义AppProviderRouters、全局样式、全局类型声明等,是应用程序的入口。
  • pages: 页面入口,将 entities(实体)features(特征)widgets(小部件) 组合起来的完整页面,对应着应用路由,这里组合成的东西提供给 app 使用
  • widgets: 小部件组合层,并非 UI Compoennts,将 entitiesfeatures 组合成有意义的业务模块,例如 用户信息卡片、问题列表、用户设置等
  • features: 与用户有交互的模块,可以为用户带来价值的行为,例如发送评论、加入购物车、搜索等,该层可根据项目具体需求选择
  • entities: 业务实体层,不同实体的定义目录,例如用户、产品、订单等,也是可选层
  • shared: 共享模块,与项目、业务的具体内容分离,可重复使用的功能,例如组件库、工具库、API 等

[!TIP] Layers 并非是固定不变的,对于不复杂的项目或者极其复杂的项目来说,可以根据项目自身需求动态的减少或者增加 Layers

分层结构FSD的一个关键特点:entities 不能使用来自 features 的功能,因为 fetures 在层次结构中层级更高,层级由高到低的顺序如图所示: CleanShot 2024-05-29 at 13.56.11@2x.png 层级低的Layer不能引用层级高的Layer,例如:

可以在 pages 目录中使用 features 或者 shared 中的模块 但是无法在 entities 中引用 features

这样做的目的是为了保持引用关系只朝一个方向线性流动,层级越低,其改动的风险就会越大,因为它相当于基础的东西,会被很多地方所使用

CleanShot 2024-05-29 at 14.09.55@2x.png

pages

pages对应着应用程序的路由结构,在 FSD 架构中,pages 之间不可以互相引用,一个页面不能从另外一个页面目录中导入代码

shared

共享层与其他层不同在于它不包含Slices,而是直接由 Segment 组成 CleanShot 2024-05-28 at 23.42.29@2x.png


  • UI: 基础组件,例如不包含业务逻辑的 ButtomInputModal 组件等
  • API: 封装的 fetch请求方法,用来与后端 API 交互
  • Config:环境变量、应用程序配置等
  • Il8n:多语言配置 (如果有的话)
  • Router:路由配置 (如果有的话)
  • Constant:业务常量
同样在 `Shared` 中,`Segment` 并不是一成不变的,可以项目需求增加或者减少,但需要注意,定义 `Segemnt` 时需要考虑的一个重点是:`Segment` 名称应该描述 **为什么**而不是 **是什么**,`components`、`hooks` 和 `models` 这样的名称尽量不要使用

Slices

Slices(切片) 由 Segment (段) 组成,主要目的是按照业务领域代码进行分区,通过将逻辑相关的模块放在一起,让代码更容易导航,切片不能在同一层上使用其他切片,这有助于实现 高内聚性低耦合性

切片中连接的并不是抽象的逻辑实物,而是具体的业务实体 (entities)

Slices 有一些需要注意的事项:

  • 同一 Layer 的 Slices 不能相互引用
  • 在大多数情况下,应该避免在 Slices 中嵌套,并且只使用文件夹进行结构分组

一个较为完整的 Slices 示例:

CleanShot 2024-05-29 at 15.48.03@2x.png

Segment

Segment 是微小的模块,目的在于帮助切片内的代码分离,最常见的 SegmentUIModel(store、actions)ApiLib,同时还可以根据需要省略一些或者添加其他模块

  • ui: 只包含 UI 视图,不包含业务逻辑
  • lib: 基础工具库
  • config:配置
  • api:api 请求的逻辑,或者 api 实例

统一导出索引、公共 API

每个 pagesslices 都应该有一个统一的对外导出索引,一般情况下为当前目录下的 index文件,在这个文件下统一进行模块的导出,与其他模块进行引用交互。而对于没有 slicesshared 层,则不需要统一导出索引,只需要为每个模块的定义单独的导出。

└── src
    ├── app
    ├── entities
    │   ├── account
    │   │   └── index.ts
    │   ├── post
    │   │   └── index.ts
    │   ├── product
    │   │   └── index.ts
    │   └── user
    │       └── index.ts
    ├── features
    │   ├── auth-login
    │   │   └── index.ts
    │   ├── create-account
    │   │   └── index.ts
    │   └── payment
    │       └── index.ts
    ├── pages
    │   ├── about
    │   │   └── index.ts
    │   ├── cart
    │   │   └── index.ts
    │   ├── home
    │   │   └── index.ts
    │   ├── posts
    │   │   └── index.ts
    │   └── user
    │       └── index.ts
    ├── shared
    │   ├── api
    │   │   └── index.ts
    │   ├── config
    │   │   └── index.ts
    │   └── ui
    │       └── index.tsx
    └── widgets
        ├── cart-box
        ├── footer
        └── header
公共 API 简化了导入和导出的工作,在对项目进行修改时,可以方便的统一修改,而不需要在代码的各个地方进行修改

常规架构

在许多语言和框架中,大家都习惯将类似功能的代码放在一起,例如

├── src
│   ├── actions
│   ├── common
│   ├── components
│   ├── controller
│   ├── pages
│   ├── services
│   └── utils
└

相信大家很熟悉这种目录结构了,常规架构简单易懂,一眼就能看出来某个模块是用来做什么的。 但是明显的缺点就是模块之间的隐式连接和模块混乱,使得项目会变得难以维护,随着时间的推移,常规架构的缺点就会变得越来越明显,项目发展的越久,架构就会越来越难以梳理和维护。

  • 有时候不清楚一个应该把功能模块放在模块还是组件中
  • 在另一个模块中使用其他模块时遇到困难
  • 存储业务实体的问题

FSD 主要解决的问题

Feature-SLiced 架构的主要目的在于实现 高内聚性低耦合性 , 它根据代码的作用以及对项目的贡献来决定它们的位置。 在 FSD 中,较低的层一般是属于比较抽象的,它们可以在较高的层中重复、多次的使用,这样就实现了 抽象多态 因为高层可以重复使用低层,所以也实现了 继承

通过公共 API index,统一模块对外的入口,限制对 Slice 和 Segment 内部的访问,实现了 封装

所以可以发现,FSD 通过约定的方式,就已经在结构层面实现了 多态封装继承抽象的概念,这些概念确保了代码的隔离、可重用性和多功能性。

FSD 通过其概念和标准,可以避免常规架构存在的问题,但是需要前期的了解

优点

统一化

  • 代码按照影响范围(Layers)、领域(Slices)、和 段(Segment)进行组织,一个标准化的架构 逻辑的受控重用
  • 每个组件模块都有其目的和可预测的依赖关系
  • 便于尊徐 DRY 原则和适应可能性之间保持平衡 功能变化和重构时的稳定性 特定层上的模块不能使用同一层上的其他模块,也不能使用上面的层,可以进行单独的、不影响其他模块的修改,从而避免产生意外的后果。 业务和用户需求导向 当应用程序被分割成业务领域时,通过浏览代码就可以发现和深入的了解项目的所有功能。

缺点

  • 与常规架构相比,有一定的上手成本
  • 需要团队统一规范和遵守
  • 不太适合 MVP 项目或者小的项目,长期来看并没有多大的好处,还需额外付出
  • 出现问题需要理科解决,而不能等到“以后”

Feature-Sliced 架构并非适用与所有的前端项目,尤其当前大多数框架、库都提供了自己的官方标准模板,例如 NextJS 和 Sveltekit 等更是对目录结构的要求有一些限制。 同时,FSD 架构的应用还需要考虑到项目、团队等诸多要素,所以 FSD 也许并不是最适合你的项目大,但是它的概念以及一些规范,则非常适合去学习和借鉴并应用到自己的项目中。

示例

TODO:占个坑,等应用了之后再来补充。

参考