前端项目目录结构规范
本规范融合了业内最新的架构理念和最佳实践,旨在提供一个清晰、可扩展且易于维护的前端项目目录结构标准。
1. 命名规范
1.1 统一命名原则
推荐统一使用 kebab-case:
- 所有文件夹:统一使用 kebab-case,如
auto-complete/、user-management/、api-client/ - 所有文件:统一使用 kebab-case,如
auto-complete.tsx、user-profile.tsx - 组件类名:使用 PascalCase,如
AutoComplete、UserProfile - 目录命名规则:
- 文件容器目录:包含多个同类文件时使用复数,如
components/、pages/、hooks/ - 功能性目录:表示功能领域或概念时使用单数,如
model/、api/、ui/、lib/ - 约定优先:遵循行业标准,而非严格的语法规则(如使用
lib/表示库目录而非library/,虽然后者语义更完整)
- 文件容器目录:包含多个同类文件时使用复数,如
- 语义化:名称应清晰表达文件/文件夹的用途
1.2 特殊文件规则
1.2.1 index.ts(Barrel 文件)
- 主要用途:重新导出模块,创建统一的导出入口
- 最佳实践:保持简单,只做导出,避免副作用
// 推荐:简单的重新导出
export { Button } from "./button";
export { Input } from "./input";
export type { ButtonProps } from "./button";
// 谨慎:有条件的导出(可能影响 Tree-shaking)
export { Button } from "./button";
export { Input } from process.env.NODE_ENV === "development" ? "./input-dev" : "./input";
// 避免:包含业务逻辑
const theme = getTheme();
export { Button } from theme === "dark" ? "./button-dark" : "./button-light";
1.2.2 其他特殊文件
- 测试文件:
- 推荐:
*.test.tsx或*.spec.tsx(工具链兼容性更好) - 可选:
*-test.tsx或*-spec.tsx(与命名规范更一致)
- 推荐:
- 类型文件:
*.types.ts或types.ts - 常量文件:
constants.ts或*.constants.ts - 配置文件:
config.ts或*.config.ts
1.3 组件类型命名约定
- UI 组件:描述性名称(如
PrimaryButton,SearchInput,DataTable) - 页面组件:组件类名以 Page 结尾(如
HomePage,ProfilePage),文件夹直接使用功能名称(如home/,profile/) - 布局组件:以 Layout 结尾(如
AppLayout,AuthLayout,DashboardLayout) - 功能组件:描述功能(如
UserProfile,FileUpload,OrderSummary) - Provider 组件:以 Provider 结尾(如
ThemeProvider,AuthProvider,DataProvider) - 高阶组件:以 with 开头(如
withAuth,withLoading,withErrorBoundary) - Hook 函数:以 use 开头(如
useAuth,useLocalStorage,useDebounce) - 工具组件:描述用途(如
ErrorBoundary,LoadingSpinner,ProtectedRoute) - 表单组件:以 Form 结尾或包含 Field(如
LoginForm,InputField,SelectField)
1.4 代码格式化规范(Prettier)
为保证团队代码风格统一,建议全项目使用 Prettier 作为代码格式化工具。
-
强制格式化:所有提交的代码必须经过 Prettier 格式化。
-
自动化集成:推荐在项目根目录添加
.prettierrc配置文件,并在package.json中配置format脚本:// package.json"scripts": {"format": "prettier --write ."} -
常用 Prettier 配置示例:
// .prettierrc{"singleQuote": true, // 使用单引号"semi": false, // 语句末尾不加分号"trailingComma": "all", // 多行时尽可能使用尾随逗号"printWidth": 100, // 每行最大长度 100"tabWidth": 2, // 缩进为 2 个空格"endOfLine": "lf" // 换行符统一为 LF} -
编辑器集成:建议在 VSCode 等编辑器中安装 Prettier 插件,启用"保存时自动格式化"。
-
CI 检查:可选,在 CI 流程中增加 Prettier 检查,防止未格式化代码合入主干。
统一的代码风格有助于提升团队协作效率,减少无意义的代码 review。
1.5 路径别名配置
在项目中启用路径别名,请参考以下配置:
// tsconfig.app.json
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["src/*"]
}
}
}
// vite.config.ts
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import { resolve } from "path";
export default defineConfig({
plugins: [react()],
resolve: {
alias: {
"@": resolve(__dirname, "src"),
},
},
});
2. 目录结构
2.1 设计原则
2.1.1 基本原则
- 关注点分离:将逻辑、UI 和数据管理分开,提高可读性和可维护性
- 模块化设计:将应用拆分为小的、可复用的组件,可独立开发和测试
- DRY 原则:避免代码重复,将通用功能抽象为可复用模块
- 功能优先:按功能而非技术类型组织代码,便于理解和维护
2.1.2 扩展性考虑
- 按功能分组:相关的组件、hooks、工具函数放在一起
- 就近原则:组件相关的样式、测试、资源文件放在同一目录
- 删除友好:删除功能时,删除对应文件夹即可移除所有相关文件
2.2 项目结构划分方式
2.2.1 按功能划分
把一个功能相关联的文件都放在一起:
feed/
index.js
Feed.js
Feed.css
FeedStory.js
FeedStory.test.js
FeedAPI.js
profile/
index.js
Profile.js
ProfileHeader.js
ProfileHeader.css
ProfileAPI.js
按功能划分结构很清晰,一个功能的代码都在一起,要删除一个功能也不用到处去找相关的文件。但是,一个功能的定义因人而异,有时一个功能的定义非常模糊,拆分起来非常困难,比如要实现一个用户评价商品的功能,是应该属于用户管理还是商品管理?
2.2.2 按文件类型划分
将同一类型的文件放一起,比如所有的 component,所有的 api:
apis/
APIUtils.js
ProfileAPI.js
UserAPI.js
components/
Avatar.js
Feed.js
FeedStory.js
Profile.js
ProfileHeader.js
按类型放一起的好处在于,不用再纠结该放哪的问题,组件都放 components 就好了,api 都放 apis 下好了,当然这失去了按功能组织的优点,因为相关文件都散落在各处。
这两种项目结构并没有绝对的好坏之分。通常,随着项目扩大,一般都是混用这两种类型。之所以有结构,更多的是一种约定,开发者共同遵守这一约定即可。
2.2.3 静态资源(Assets)
静态资源的组织方式应遵循"全局集中 + 就近分散"的原则,根据资源的使用范围和业务相关性来决定存放位置。
2.2.3.1 全局静态资源
位置: src/shared/assets/
职责: 存放全局可复用、无业务耦合的静态资源
src/shared/assets/
├── images/ # 全局图片资源
│ ├── logo/ # 品牌 Logo
│ │ ├── logo.svg
│ │ ├── logo-dark.svg
│ │ └── favicon.ico
│ ├── icons/ # 通用图标集
│ │ ├── arrow-right.svg
│ │ ├── search.svg
│ │ └── user.svg
│ ├── illustrations/ # 通用插画
│ │ ├── empty-state.svg
│ │ ├── error-404.svg
│ │ └── loading.svg
│ └── placeholders/ # 占位图片
│ ├── avatar-default.png
│ └── image-placeholder.svg
├── fonts/ # 字体文件
│ ├── inter/
│ └── roboto/
├── videos/ # 视频文件
│ └── hero-background.mp4
└── index.ts # 资源路径统一导出
导出示例:
// src/shared/assets/index.ts
export const IMAGES = {
LOGO: "/images/logo/logo.svg",
LOGO_DARK: "/images/logo/logo-dark.svg",
EMPTY_STATE: "/images/illustrations/empty-state.svg",
DEFAULT_AVATAR: "/images/placeholders/avatar-default.png",
} as const;
export const ICONS = {
SEARCH: "/images/icons/search.svg",
USER: "/images/icons/user.svg",
ARROW_RIGHT: "/images/icons/arrow-right.svg",
} as const;
2.2.3.2 业务相关静态资源
位置: 各层内的 assets/ 目录
职责: 存放与特定业务功能强相关的静态资源
# Entity 层示例
entities/product/
├── assets/
│ └── images/
│ ├── product-placeholder.svg # 商品占位图
│ └── brand-badges/ # 品牌徽章
└── ui/
└── product-card.tsx # 使用上述资源
# Feature 层示例
features/user-auth/
├── assets/
│ └── images/
│ ├── login-illustration.svg # 登录页插画
│ └── social-icons/ # 社交登录图标
└── ui/
└── login-form.tsx # 使用上述资源
# Widget 层示例
widgets/header/
├── assets/
│ └── images/
│ └── navigation-icons/ # 导航专用图标
└── ui/
└── header.tsx # 使用上述资源
3. 推荐的项目结构划分
项目结构划分个人更倾向以 Feature-Sliced Design (FSD)为基础,src 目录大概分为以下六层:
src/
├── app/ # 应用层-第一层:全局配置和初始化
├── pages/ # 页面层-第二层:路由级页面组件
├── widgets/ # 组件层-第三层:复合业务组件
├── features/ # 功能层-第四层:功能模块
├── entities/ # 实体层-第五层:业务实体
└── shared/ # 共享层-第六层:可复用基础设施
3.1 架构层规则
3.1.1. 依赖方向
上层可以使用下层,下层不能使用上层。
-
分层依赖关系图
┌─────────────┐│ app/ │ ← 最上层:应用入口,可以使用所有下层└─────────────┘↓┌─────────────┐│ pages/ │ ← 第二层:页面层,可以使用下面4层└─────────────┘↓┌─────────────┐│ widgets/ │ ← 第三层:组件层,可以使用下面3层└─────────────┘↓┌─────────────┐│ features/ │ ← 第四层:功能层,可以使用下面2层└─────────────┘↓┌─────────────┐│ entities/ │ ← 第五层:实体层,只能使用最下层└─────────────┘↓┌─────────────┐│ shared/ │ ← 最下层:共享层,不依赖任何上层└─────────────┘ -
依赖示例说明
// 正确:pages 层使用 widgets 层// pages/home/index.tsximport { ProductList } from "@/widgets/product-list";import { Header } from "@/widgets/header";// 正确:features 层使用 entities 层和 shared 层// features/shopping-cart/ui/cart-item.tsximport { Product } from "@/entities/product";import { Button } from "@/shared/ui";// 正确:entities 层使用 shared 层// entities/user/api/user-api.tsimport { httpClient } from "@/shared/api";// 错误:下层不能使用上层// shared/ui/button.tsximport { useAuth } from "@/features/auth"; // 错误!shared 不能使用 features
3.1.2 同层隔离
同一层的模块不能直接相互依赖。例如:
// 错误:同层不能直接相互依赖
// features/auth/model/auth-store.ts
import { cartStore } from "@/features/shopping-cart"; // 错误!同层直接依赖
// 正确:同层通过共享层通信
// features/auth/model/auth-store.ts
import { eventBus } from "@/shared/lib/event-bus";
eventBus.emit("user-logged-in", userData);
// features/shopping-cart/model/cart-store.ts
import { eventBus } from "@/shared/lib/event-bus";
eventBus.on("user-logged-in", handleUserLogin);
3.1.3 Public API
Features 层、Entities 层和 Shared 层的模块需要在每个切片下创建 index.ts 文件来暴露 Public API,而不是在这些层直接创建 index.ts:
-
Features 层
// features/product-list/index.tsexport { ProductList } from "./ui";export { useProducts } from "./model"; -
Entities 层
// entities/user/index.tsexport type { User } from "./model";export { UserCard } from "./ui"; -
Shared 层
// shared/ui/index.tsexport { Button } from "./button";export { Input } from "./input";
3.2 层详解
3.2.1 app/ - 应用层(最上层)
App 层负责处理所有应用级事务,包括技术层面(如上下文提供者 providers)和业务层面(如埋点、全局错误处理、全局权限守卫、统计 analytics)的全局配置。
以下是一个基础结构(可以根据项目需求动态添加或移除目录)示例:
app/
├── providers/ # 全局提供者
│ ├── theme-provider.tsx # 主题提供者
│ ├── auth-provider.tsx # 认证提供者
│ ├── router-provider.tsx # 路由提供者
│ └── index.ts # 提供者统一导出
├── styles/ # 全局样式
│ ├── globals.css # 全局基础样式
│ ├── variables.css # CSS变量定义
│ ├── reset.css # 样式重置
│ └── themes/ # 主题相关样式
│ ├── light.css
│ ├── dark.css
│ └── index.ts
├── store/ # 全局状态管理
│ ├── middleware.ts # 中间件配置
│ ├── root-reducer.ts # 根reducer
│ └── index.ts # Store入口
├── routing/ # 路由相关
│ ├── routes.tsx # 路由组件
│ ├── guards.tsx # 路由守卫
│ ├── lazy-imports.ts # 懒加载导入
│ └── index.ts
├── app.tsx # 应用层统一导出
└── index.ts # 应用入口组件
app 层示例:
// app/index.ts - 只需要导出 App
export { App } from "./app";
// app/app.tsx - 应用入口组件
import { ThemeProvider } from "./providers/theme-provider";
import { AuthProvider } from "./providers/auth-provider";
import { RouterProvider } from "./providers/router-provider";
import { AppRoutes } from "./routing";
import "./styles/globals.css";
export function App() {
return (
<ThemeProvider>
<AuthProvider>
<RouterProvider>
<AppRoutes />
</RouterProvider>
</AuthProvider>
</ThemeProvider>
);
}
3.2.2 pages/ - 页面层
应用的页面级组件,对应路由,主要负责页面级组件的组装和简单的状态协调:
- 职责清晰: 只负责组装和编排各个功能模块
- 保持简单: 不包含复杂的业务逻辑,复杂逻辑下沉到 Features 层
- 状态管理: 只处理页面级的简单状态,复杂状态管理在 Features 层实现
- 路由处理: 负责处理页面级的路由参数和跳转
pages/
├── home/
├── product/
│ ├── index.ts # 导出product.tsx
│ ├── product.tsx # 实际的页面组件
│ ├── product.module.css # 页面样式
│ └── ui/ # 页面专用组件
│ ├── product-gallery.tsx # 商品图片画廊
│ ├── product-info.tsx # 商品信息区域
│ └── related-products.tsx # 相关商品推荐
└── not-found/
Pages 层示例:
// pages/product/product.tsx
import { ProductDetail } from "@/features/product-detail";
import { ProductReviews } from "@/features/product-reviews";
export function ProductPage() {
return (
<div className="product-page">
<ProductDetail />
<ProductReviews />
</div>
);
}
3.2.3 widgets/ - 组件层
widgets 层用于存放大型、自给自足的 UI 模块。这些模块通常是复合业务组件,可以组合多个 features 和 entities,形成完整的功能单元。例如页头、侧边栏、商品列表等跨页面复用的大型 UI 组件。
特点:
- 独立性强(通常包含完整的 UI + 数据获取 + 加载状态 + 错误边界)。
- 常用于跨页面复用的大块 UI。
- 或者某个页面里有多个“大块 UI”,可以拆成 Widgets。
widgets/
├── header/ # 页头组件
├── product-search-list/ # 商品搜索
└── notification-center/ # 通知中心 Widget
-
示例目录骨架(推荐):
<widget>/├── index.ts # Public API(仅导出)├── ui/ # 组件组合与视图├── model/ # 状态与派生逻辑(可选)├── api/ # 数据获取(可选)├── lib/ # 内部工具(可选)└── __tests__/ # 测试(可选)提示:Widget 通过自身 Public API 向外暴露必要能力,内部实现(各段)不被外部直接引用。
-
最小示例(仅示意):
// widgets/product-search-list/index.tsexport { ProductSearchList } from "./ui/product-search-list";export * as ProductSearchListModel from "./model";// widgets/product-search-list/ui/product-search-list.tsximport { useEffect } from "react";import { useProductSearchList } from "../model/use-product-search-list";export function ProductSearchList() {const { items, load } = useProductSearchList();useEffect(() => {load();}, [load]);return (<ul>{items.map((p) => (<li key={p.id}>{p.name}</li>))}</ul>);}// widgets/product-search-list/model/use-product-search-list.tsimport { useCallback, useState } from "react";import { fetchProducts } from "../api/fetch-products";export function useProductSearchList() {const [items, setItems] = useState<Array<{ id: string; name: string }>>([]);const load = useCallback(async () => {const data = await fetchProducts();setItems(data);}, []);return { items, load };}// widgets/product-search-list/api/fetch-products.tsexport async function fetchProducts() {// 仅示意return Promise.resolve([{ id: "1", name: "Demo Product" }]);}
3.2.4 features/ - 功能层
3.2.4.1 Features 层的职责
-
业务逻辑封装 :包含完整的功能实现
-
数据管理 :负责自己的数据获取、状态管理
-
自包含性 :feature 应该是独立、可复用的
-
示例目录骨架(推荐):
<feature>/├── index.ts # Public API(仅导出)├── ui/ # 视图组件(Feature 级复合组件)├── model/ # 状态、selectors、副作用├── api/ # 与该 Feature 相关的 API 客户端├── lib/ # 仅供该 Feature 内部复用的工具├── assets/ # 仅该 Feature 内部使用的资源(图片等)└── __tests__/ # 单测或集成测试
提示:外部模块仅依赖该 Feature 的 Public API,禁止直接引用内部实现文件(各段)。
-
最小示例(仅示意):
// features/product-search/index.tsexport { SearchPanel } from "./ui/search-panel";export { useProductSearch } from "./model/use-product-search";// features/product-search/ui/search-panel.tsximport { useProductSearch } from "../model/use-product-search";export function SearchPanel() {const { query, results, setQuery, search } = useProductSearch();return (<div><input value={query} onChange={(e) => setQuery(e.target.value)} /><button onClick={search}>Search</button><ul>{results.map((r) => (<li key={r.id}>{r.name}</li>))}</ul></div>);}// features/product-search/model/use-product-search.tsimport { useCallback, useState } from "react";import { searchProducts } from "../api/search";export function useProductSearch() {const [query, setQuery] = useState("");const [results, setResults] = useState<Array<{ id: string; name: string }>>([]);const search = useCallback(async () => {const data = await searchProducts(query);setResults(data);}, [query]);return { query, results, setQuery, search };}// features/product-search/api/search.tsexport async function searchProducts(q: string) {// 仅示意:真实项目请接入 shared/api 或具体客户端if (!q) return [];return Promise.resolve([{ id: "1", name: `Result for ${q}` }]);}// features/product-search/ui/logo-usage.tsx(可选示例:本地 assets 仅供本 Feature 使用)import Logo from "../assets/logo.svg";export function LogoUsage() {return <img src={Logo} alt="logo" />;}
3.2.4.2 Feature 的定义原则
-
什么是 Feature?
- 用户价值导向:能为用户提供具体价值的功能单元
- 业务完整性:包含完整的业务流程,从用户交互到数据处理
- 独立性:可以独立开发、测试、部署的功能模块
-
Feature 划分标准:
按用户故事划分:
好的划分:- auth/ # "用户可以登录/注册"- product-search/ # "用户可以搜索商品"- shopping-cart/ # "用户可以管理购物车"- order-checkout/ # "用户可以下单结账"错误划分:- forms/ # 太技术化,不是用户功能- buttons/ # 太细粒度,不是完整功能- api-calls/ # 技术实现,不是用户价值按业务边界划分:
电商项目示例:features/├── user-auth/ # 用户认证相关├── product-catalog/ # 商品浏览相关├── shopping-cart/ # 购物车相关├── payment-processing/ # 支付处理相关├── user-profile/ # 用户资料相关└── review-rating/ # 评价评分相关 -
Feature 划分的实际例子:购物平台
features/├── user-auth/ # 用户登录注册├── product-search/ # 商品搜索├── shopping-cart/ # 购物车└── order-checkout/ # 订单结账 -
具体实现示例:
// features/product-search/export const ProductSearch = {SearchBar: () => <SearchInput />,SearchResults: () => <ProductList />,useProductSearch: () => {/* 搜索逻辑 */},searchProducts: (query: string) => {/* 搜索API */},};// features/shopping-cart/export const ShoppingCart = {CartIcon: () => <CartButton />,CartDrawer: () => <CartSidebar />,useCart: () => {/* 购物车状态 */},addToCart: (productId: string) => {/* 添加商品 */},}; -
页面中使用:
// pages/home/home.tsximport { ProductSearch } from "@/features/product-search";import { ShoppingCart } from "@/features/shopping-cart";export function HomePage() {return (<div><header><ProductSearch.SearchBar /><ShoppingCart.CartIcon /></header><main><ProductSearch.SearchResults /></main></div>);}
3.2.4.3 购物平台 Feature 划分标准
-
用户价值测试
正确示例:- "用户可以搜索商品" → product-search- "用户可以管理购物车" → shopping-cart- "用户可以登录注册" → user-auth- "用户可以下单结账" → order-checkout错误示例:- "用户可以使用搜索框" → 太技术化- "用户可以点击按钮" → 不是业务价值- "用户可以看到商品" → 只是展示 -
功能完整性测试 - 每个 Feature 应包含完整的用户交互流程:
product-search/├── 搜索输入 → 搜索执行 → 结果展示 → 筛选排序shopping-cart/├── 添加商品 → 查看购物车 → 修改数量 → 删除商品user-auth/├── 注册 → 登录 → 密码重置 → 退出登录 -
业务边界测试
边界清晰:- product-search: 只负责商品搜索相关- shopping-cart: 只负责购物车管理- order-checkout: 只负责订单结账流程边界模糊:- product-management: 太宽泛,包含搜索、详情、评价等- user-operations: 太宽泛,包含登录、资料、订单等 -
团队协作测试
可并行开发:- 搜索团队 → product-search- 购物车团队 → shopping-cart- 支付团队 → order-checkout- 用户团队 → user-auth每个团队可以独立开发、测试、部署自己的 Feature -
数据依赖测试
依赖关系清晰:product-search → 依赖 entities/productshopping-cart → 依赖 entities/product, entities/userorder-checkout → 依赖 shopping-cart, entities/order循环依赖:Feature A 依赖 Feature B,同时 Feature B 依赖 Feature A
3.2.5 entities/ - 实体层
业务领域中的核心数据对象,代表现实世界中具有独立身份的业务概念。
3.2.5.1 什么是 Entities?
简单理解:Entities 就是你的应用中的核心数据对象。
比如在购物网站中:
- 用户(User) - 包含姓名、邮箱、头像等信息
- 商品(Product) - 包含标题、价格、图片等信息
- 订单(Order) - 包含商品列表、总价、状态等信息
举个例子:
// 不好的做法:在每个功能中重复定义用户数据
// features/user-profile/user-profile.tsx
interface User {
id: string;
name: string;
email: string;
}
// features/shopping-cart/cart.tsx
interface User {
// 重复定义!
id: string;
name: string;
email: string;
}
// 好的做法:在 entities 中统一定义
// entities/user/model/types.ts
export interface User {
id: string;
name: string;
email: string;
}
// features/user-profile/user-profile.tsx
import { User } from "@/entities/user"; // 直接使用
// features/shopping-cart/cart.tsx
import { User } from "@/entities/user"; // 复用相同定义
3.2.5.2 目录结构
entities/
├── user/ # 用户实体
├── product/ # 商品实体
├── order/ # 订单实体
└── category/ # 商品分类实体
-
示例目录骨架(推荐):
<entity>/├── index.ts # Public API(仅导出)├── ui/ # 实体展示组件(可选)├── model/ # 实体数据模型、工具函数├── api/ # 实体相关的 API 请求函数(可选)├── lib/ # 仅供该实体内部复用的工具(可选)└── __tests__/ # 测试(可选)提示:Entity 通过自身 Public API 向外暴露必要能力,内部实现(各段)不被外部直接引用。
-
具体示例:用户实体
// entities/user/model/types.tsexport interface User {id: string;name: string;email: string;avatar?: string;memberLevel: "NORMAL" | "VIP" | "PREMIUM";createdAt: Date;updatedAt: Date;}// entities/user/model/utils.tsexport const UserUtils = {getFullName: (user: User) => user.name,isVip: (user: User) => user.memberLevel !== "NORMAL",getAvatarUrl: (user: User) => user.avatar || "/default-avatar.png",};// entities/user/api/api.tsexport const UserAPI = {getUser: (id: string): Promise<User> =>fetch(`/api/users/${id}`).then((res) => res.json()),updateUser: (id: string, data: Partial<User>): Promise<User> =>fetch(`/api/users/${id}`, {method: "PUT",body: JSON.stringify(data),}).then((res) => res.json()),};// entities/user/ui/avatar.tsxexport function UserAvatar({ user, size = "medium" }: UserAvatarProps) {return (<imgsrc={UserUtils.getAvatarUrl(user)}alt={user.name}className={`avatar avatar-${size} ${UserUtils.isVip(user) ? "vip" : ""}`}/>);}// entities/user/index.tsexport { User } from "./model/types";export { UserUtils } from "./model/utils";export { UserAPI } from "./api/api";export { UserAvatar } from "./ui/avatar";
3.2.6 shared/ - 共享层
可复用的基础设施,通常(不绝对)不包含业务逻辑:
shared/
├── assets/ # 全局可复用的静态资源(图片、图标、字体、视频)
│ ├── images/
│ ├── icons/
│ ├── fonts/
│ ├── videos/
│ └── index.ts # 统一导出
├── ui/ # 基础 UI 组件库
│ ├── button/
│ ├── input/
│ ├── modal/
│ └── index.ts
├── lib/ # 通用工具库(utils、hooks、validation、mappers...)
│ ├── utils/
│ ├── hooks/
│ ├── validation/
│ ├── mappers/
│ └── index.ts
├── api/ # 基础 API 配置与客户端
│ ├── base.ts
│ ├── types.ts
│ └── index.ts
├── config/ # 基础配置(环境、常量、路由)
│ ├── env.ts
│ ├── constants.ts
│ ├── routes.ts
│ └── index.ts
└── i18n # 多语言
├── i18n.ts
├── locales/
└── index.ts
4. FAQ
Q: 同层模块之间如何通信?
- A: 通过上层编排(pages/widgets)传递 props,或抽象出
shared/lib事件总线/服务进行解耦,避免直接相互导入。
Q: 自定义 Hook 放哪?
- A: 跨业务通用 Hook 放
shared/lib/hooks;仅在某个 feature 内使用的 Hook 放该 feature 的model/。
Q: 什么时候该创建 Widget?
- A: 当一个 UI 模块体量较大、需跨页面复用且通常自带数据加载/空态/错误处理时,优先抽为
widgets/。
Q: 实体类型定义放哪里?
- A: 放在
entities/*/model,由各层通过entities/*/index.ts统一导出与复用。
Q: 路由与守卫放哪?
- A: 放
app/routing(如routes.tsx、guards.tsx),由app统一装配。
5. 参考资源
5.1 业内标准文档
-
Feature-Sliced Design - 现代前端架构方法论
-
Bulletproof React - 生产级 React 应用架构
-
React 官方文档 - React 官方最佳实践
5.2 知名项目参考
- React DevTools - Meta 官方项目结构
- Remix - 现代全栈框架结构
- Next.js - 流行 React 框架结构
- Vite - 现代构建工具
5.3 社区资源
- React Handbook - React 项目标准
- 2025 年 React 文件夹结构 - 最新推荐结构
- 可扩展 React 应用架构 - 可扩展架构指南
- React 项目结构最佳实践 - 2025 年实用指南
5.4 工具和模板
- Create React App - 官方脚手架
- Vite React Template - 现代开发模板
- React TypeScript Cheatsheets - TypeScript 最佳实践
- ESLint React Plugin - React 代码规范
本规范基于业内最佳实践制定,会根据社区发展和团队反馈持续更新。
附录 A. 术语约定
- 层(Layer):如 app / shared / entities / features / widgets / pages。
- 切片(Slice):在 entities、features、widgets、pages 层内按业务或领域划分的子目录,如 entities/user、features/product-search、widgets/header、pages/home。
- 段(Segment):切片内的功能分段,如 ui / model / api / lib / assets / config。
- Public API:对外暴露的公共接口(通常由 index.ts 统一导出)。
- Barrel 文件:只做导出的聚合文件(通常为 index.ts),避免包含副作用。