拓冰建站拓冰建站
首页 / 资讯中心 / 正文

极速理解FSD-features-sliced design

FSD简介Feature-Sliced DesignFSD是一种用于构建前端应用程序的架构方法论。简单来说它是组织代码的规则和约定的汇编。该方法论的主要目的是在不断变化的业务需求面前使项目更加易于理解和稳定。详情可见: https://feature-sliced.design/zh/docs/get-started/overview什么样的项目需要使用如果你的项目是中大型项目使用传统的前端结构会发现进行局部修改定位或局部添加功能都会越来越困难因为一个script里会有多个逻辑处理内容而且文件名称会涵盖一个大类没有细致的区分最后定位和修改会更加困难。拆分讲解如果你的项目是前面说到的情况那就可以使用fsd来进行结构优化了所有的优化都围绕一点业务驱动首先讲App 和 sharedApp就是把全局配置、路由这些开局就要配置好的内容统一放里面来管理 。shared主要就是放通用性的东西例如全局通用组件、接口fetch封装等。它们两个内部都没有sliced因为都是通用的而不会和某一块的业务单独绑定所以它们的内部直接是段segment官方没有规定里面必须放哪些内容只是大致划分所以需要团队内部进行划分然后遵守这才是重点。widgets当多个组件相关内容被需要被同一页面使用时里面会包括多重导入、逻辑处理这些。但是我们要保证页面的内容保持简洁使用这时候就需要将这些内容放到一个widget里让页面使用这个widget即可这样总逻辑在widgets处理各自的内部逻辑还是在对应的feature里而页面依然是只处理页面级的内容保持各自的分工明确。层级职责entities纯数据展示无交互逻辑features独立的用户交互自包含业务逻辑widgets组合entities features定义「这块 UI 长什么样」pages使用 widgets无需组合ui则直接使用features只处理页面级逻辑数据获取、路由、布局骨架以下是一个实际例子例子包含了pages、widgets、features、entities四层场景Feed 页面中的文章卡片卡片包含entities/user — 作者头像和名字纯展示entities/article — 文章标题和摘要纯展示features/like-article — 点赞按钮带交互逻辑features/bookmark-article —收藏按钮带交互逻辑features/share-article — 分享按钮带交互逻辑entitied层这里的组件可能会被几处页面/组件都用到是共用但不通用的组件!-- entities/user/ui/UserBadge/UserBadge.vue -- script setup langts import type { User } from ../../model/types; defineProps{ user: User; }(); /script template div classflex items-center gap-2 img :srcuser.avatar classw-8 h-8 rounded-full / span classfont-medium{{ user.name }}/span /div /template!-- entities/article/ui/ArticlePreview/ArticlePreview.vue -- script setup langts import type { Article } from ../../model/types; defineProps{ article: Article; }(); /script template div h3 classtext-lg font-bold{{ article.title }}/h3 p classtext-gray-600{{ article.summary }}/p /div /templatefeatures 层这里面就是针对具体业务的组件与逻辑处理针对性很强点击喜欢组件以及逻辑处理// features/like-article/model/useToggleLike.tsimport{ref}fromvue;exportfunctionuseToggleLike(articleId:string,initialLiked:boolean){constlikedref(initialLiked);constcountref(0);// 实际应从 API 获取consttoggleasync(){liked.value!liked.value;count.valueliked.value?1:-1;// await api.likeArticle(articleId, liked.value);};return{liked,count,toggle};}!-- features/like-article/ui/LikeButton/LikeButton.vue -- script setup langts import { useToggleLike } from ../../model/useToggleLike; const props defineProps{ articleId: string; initialLiked: boolean; }(); const { liked, count, toggle } useToggleLike(props.articleId, props.initialLiked); /script template button clicktoggle :class{ text-red-500: liked } ♥ {{ count }} /button /template点击收藏组件以及逻辑处理// features/bookmark-article/model/useToggleBookmark.tsimport{ref}fromvue;exportfunctionuseToggleBookmark(articleId:string,initialBookmarked:boolean){constbookmarkedref(initialBookmarked);consttoggleasync(){bookmarked.value!bookmarked.value;// await api.bookmarkArticle(articleId, bookmarked.value);};return{bookmarked,toggle};}!-- features/bookmark-article/ui/BookmarkButton/BookmarkButton.vue -- script setup langts import { useToggleBookmark } from ../../model/useToggleBookmark; const props defineProps{ articleId: string; initialBookmarked: boolean; }(); const { bookmarked, toggle } useToggleBookmark(props.articleId, props.initialBookmarked); /script template button clicktoggle {{ bookmarked ? 已收藏 : 收藏 }} /button /template分享文章组件以及逻辑处理// features/share-article/model/useShare.tsexportfunctionuseShare(articleId:string){constshare(){// 实际应调用分享 APIconsole.log(分享文章:,articleId);};returnshare;}!-- features/share-article/ui/ShareButton/ShareButton.vue -- script setup langts import { useShare } from ../../model/useShare; const props defineProps{ articleId: string; }(); const share useShare(props.articleId); /script template button clickshare↗ 分享/button /templatewidgets 层文章展示组件需集成上面的所有组件!-- widgets/article-card/ui/ArticleCard/ArticleCard.vue -- script setup langts import { UserBadge } from /entities/user; import { ArticlePreview } from /entities/article; import { LikeButton } from /features/like-article; import { BookmarkButton } from /features/bookmark-article; import { ShareButton } from /features/share-article; import type { Article } from /entities/article; defineProps{ article: Article; }(); /script template article classborder rounded-xl p-4 space-y-3 UserBadge :userarticle.author / ArticlePreview :articlearticle / div classflex items-center gap-4 pt-2 LikeButton :article-idarticle.id :initial-likedarticle.isLiked / BookmarkButton :article-idarticle.id :initial-bookmarkedarticle.isBookmarked / ShareButton :article-idarticle.id / /div /article /templatepages 层最新文章页面页面依然只处理页面级的逻辑!-- pages/feed/ui/FeedPage/FeedPage.vue -- script setup langts import { useArticles } from ../model/useArticles; import { ArticleCard } from /widgets/article-card; const { articles, isLoading } useArticles(); /script template main classmax-w-2xl mx-auto space-y-4 h1最新文章/h1 div v-ifisLoading加载中.../div template v-else ArticleCard v-forarticle in articles :keyarticle.id :articlearticle / /template /main /template// pages/feed/model/useArticles.tsimport{ref,onMounted}fromvue;importtype{Article}from/entities/article;exportfunctionuseArticles(){constarticlesrefArticle[]([]);constisLoadingref(true);onMounted(async(){// const data await api.fetchArticles();// articles.value data;isLoading.valuefalse;});return{articles,isLoading};}features上面的例子有关于features的它就是根据某一块具体的页面进行组件、逻辑、接口的处理倘若里面的内容需要被此feature外的内容使用就说明这一块内容需要提升至entities层了通用则需要放到shared层了。点击喜欢组件以及逻辑处理这里不会涉及其他业务相关的// features/like-article/model/useToggleLike.tsimport{ref}fromvue;exportfunctionuseToggleLike(articleId:string,initialLiked:boolean){constlikedref(initialLiked);constcountref(0);// 实际应从 API 获取consttoggleasync(){liked.value!liked.value;count.valueliked.value?1:-1;// await api.likeArticle(articleId, liked.value);};return{liked,count,toggle};}!-- features/like-article/ui/LikeButton/LikeButton.vue -- script setup langts import { useToggleLike } from ../../model/useToggleLike; const props defineProps{ articleId: string; initialLiked: boolean; }(); const { liked, count, toggle } useToggleLike(props.articleId, props.initialLiked); /script template button clicktoggle :class{ text-red-500: liked } ♥ {{ count }} /button /templateentities速记 公用但不通用处于features与shared之间但是其内涵是业务实体一般来说entities里的组件都为展示类的多处展示而features里的组件一般会发生数据操作与业务强关联所以一般不会公用。entitiesfeatures本质业务实体是什么用户行为做什么核心问题“文章长什么样”“用户对文章做了什么”有无交互❌ 纯展示无用户动作✅ 有用户触发的事件有无副作用❌ 只读✅ 会改状态、调接口三个关键层对比内核对比维度entitiesfeatureswidgets本质名词Article, User动词like, bookmark组合体Card, Header有无交互❌ 纯展示✅ 有用户触发的事件⚠️ 本身无新交互只转发状态管理被动接收props主动管理内部状态不管理状态只传递网络请求主要是GET读主要是POST/PUT/DELETE写不直接发请求复用范围全局通用场景化复用页面级复用依赖方向不依赖任何层可依赖 entities可依赖 entities features测试重点渲染正确性交互逻辑 API 调用组合正确性 样式结构对比entities/article/ features/like-article/ widgets/article-card/ ├── model/ ├── model/ ├── model/ │ ├── types.ts ⬅️ 类型 │ ├── useToggleLike.ts ⬅️ 逻辑 │ └── (通常为空或极薄) │ └── stores/ ├── types.ts ├── ui/ ├── ui/ │ ├── ArticlePreview.vue │ └── LikeButton.vue ├── ui/ │ ├── ArticleList.vue ├── api/ └── ArticleCard.vue │ └── ArticleSkeleton.vue │ └── likeApi.ts ├── api/ ├── api/ └── lib/ │ └── (无) │ └── articleApi.ts ⬅️ GET └── optimisticUpdate.ts └── lib/ └── lib/ └── (无) └── formatDate.ts具体的segment可以内部进行定义并非固定。
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门