基于功能的文件夹结构
按功能领域而不是按类型组织代码,将测试、样式和组件放在一起,并使用 eslint-plugin-boundaries 强制执行边界。
基于功能的文件夹结构 是 CoddyKit 上的免费 Frontend Academy 课时。 这是第 4 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Frontend Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Frontend Academy 课程共包含 4 节课。
组织代码的两种方式
您可以按类型分组代码(components/、hooks/、services/、types/),也可以按功能分组(auth/、checkout/、dashboard/——每个目录都包含自己的组件、钩子等)。对于中大型应用,按功能组织更有优势。
按类型组织:默认陷阱
经典的 React 教程结构会按类型将所有内容分组。这种结构扩展性很差:每次修改文件都要涉及多个不相关的文件夹,而“查找所有结账代码”意味着要在整个目录树中搜索。
// Type-based (avoid for large apps):
src/
components/
Button.tsx
LoginForm.tsx
CartItem.tsx
hooks/
useAuth.ts
useCart.ts
services/
auth.ts
cart.ts
types/
User.ts
CartItem.ts按功能组织的结构
将与某项功能相关的所有内容放在同一个文件夹中。容易查找、容易删除,也容易理解。
src/
features/
auth/
LoginForm.tsx
SignupForm.tsx
useAuth.ts
auth.service.ts
auth.types.ts
auth.test.tsx
cart/
CartItem.tsx
CartSummary.tsx
useCart.ts
cart.service.ts
cart.types.ts
shared/
components/
Button.tsx
hooks/
useDebounce.ts功能内部的就近组织
一个功能文件夹包含:组件、钩子、服务、类型和测试——该领域所需的全部代码。新开发者想了解身份验证?打开 auth/。想删除身份验证?删除这个文件夹即可。
共享代码与功能代码
被两个或更多功能使用的代码移到 shared/(或 lib/)中。只被一个功能使用的代码留在该功能内部。不要因为“以后可能复用”就提前抽取——等到第二次使用时再抽取。
功能边界规则
功能不应直接相互导入。如果两个功能需要共享代码,就将共享部分移到 shared/。如果它们需要协调,就使用事件或应用层的共享存储。
使用 ESLint 强制执行边界
使用 eslint-plugin-boundaries 或 eslint-plugin-import 强制执行以下规则:功能可以从 shared 导入,但不能相互导入。
// .eslintrc.json
{
"plugins": ["boundaries"],
"settings": {
"boundaries/elements": [
{ "type": "feature", "pattern": "src/features/*" },
{ "type": "shared", "pattern": "src/shared/*" }
]
},
"rules": {
"boundaries/element-types": ["error", {
"default": "disallow",
"rules": [
{ "from": "feature", "allow": ["shared"] },
{ "from": "shared", "allow": ["shared"] }
]
}]
}
}每个功能的公共 API
每个功能都通过 features/auth/index.ts 导出公共 API。其他代码从 '@/features/auth' 导入,而不是使用深层路径。这样可以重构内部实现,而不会破坏使用方。
// features/auth/index.ts
export { LoginForm } from './LoginForm';
export { useAuth } from './useAuth';
export type { User, AuthState } from './auth.types';
// Consumers:
import { LoginForm, useAuth } from '@/features/auth';
// NOT: import { LoginForm } from '@/features/auth/LoginForm';嵌套子功能
大型功能可以包含子文件夹:dashboard/widgets/、dashboard/charts/。避免嵌套超过 2—3 层,否则搜索会变得非常痛苦。
路由放在哪里
页面和路由可以放在顶层的 pages/ 或 routes/ 文件夹中。它们应保持精简:从功能中引入组件和钩子,再将它们组合起来。
src/
pages/
Dashboard.page.tsx // composes Dashboard widgets from features/dashboard/
Cart.page.tsx // composes from features/cart/
features/
shared/迁移现有应用
只从新功能开始——将新代码放入 features/。不要一次性重构所有内容。修改旧文件时,再逐步迁移它们。ESLint 边界规则可以防止新结构逐渐退化。
按类型组织仍然适用的情况
对于非常小的应用(组件少于 30 个)或库,按类型组织没有问题。一旦出现明确的业务领域(身份验证、结账、账单、设置),就应采用按功能组织的方式。
快速检查
与按文件类型分组相比,按功能分组代码的主要组织优势是什么?
回顾:按功能组织的结构
features/ 包含特定领域的代码;shared/ 包含跨功能的工具。每个功能都导出一个公共 index.ts。功能之间不能相互导入,只能从 shared 导入。使用 eslint-plugin-boundaries 强制执行这些规则。页面和路由负责组合各个功能。应逐步迁移。对于小型应用,按类型组织没有问题。
常见问题解答
「基于功能的文件夹结构」课时是免费的吗?
是的 — 「基于功能的文件夹结构」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Frontend Academy 课程的其余内容,请升级到 CoddyKit PRO。 Frontend Academy 课程共包含 4 节课。
「基于功能的文件夹结构」这节课中我会学到什么?
按功能领域而不是按类型组织代码,将测试、样式和组件放在一起,并使用 eslint-plugin-boundaries 强制执行边界。 你通过在浏览器中直接运行的动手代码来练习 Frontend Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 Frontend Academy 需要有经验吗?
无需任何先前经验。CoddyKit 上的 Frontend Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 4 节课,共 4 节。
「基于功能的文件夹结构」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 Frontend Academy 课中编写并运行代码吗?
能。每节 Frontend Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。