前端项目目录规范设计与实战经验分享

司马绍博 前端 阅读 1,623
赞 20 收藏
二维码
手机扫码查看
反馈

为什么我开始纠结目录规范这事儿?

说实话,以前写项目根本没怎么管目录结构,想到哪建到哪。结果去年接手一个三年老项目,光是找某个组件的样式文件就花了二十分钟——因为有人把 CSS 放在 src/components/ui/,有人放 src/styles/partials/,还有人直接塞进组件同级目录。那一刻我发誓:下一个项目,必须定死目录规范。

前端项目目录规范设计与实战经验分享

但问题来了:用哪种?网上一搜,主流方案就那么几种,可每种都说自己“清晰”“可维护”。我折腾了几个小项目后,发现真不是谁吹得响就谁好用。今天就聊聊我踩过的坑,以及我现在到底怎么选。

“约定大于配置”派:Next.js 默认结构

如果你用 Next.js,大概率会默认接受它的目录规范。比如:

src/
├── app/               # App Router
│   ├── layout.tsx
│   ├── page.tsx
│   └── (marketing)/   # 路由组
├── components/
├── lib/
└── styles/

这套结构的好处是开箱即用,不用动脑子。Next.js 官方文档手把手教你这么干,工具链也全适配。我刚开始用时觉得贼省心——页面、布局、API 路由都有固定位置,新人上手快。

但用久了就难受。比如我想把某个页面相关的 hooks、utils、types 全部聚在一起,形成“功能内聚”,但 Next.js 的约定强制你把 hooks 放到 lib/hooks/,导致跨文件跳转频繁。更烦的是,一旦你想自定义路由逻辑(比如动态嵌套路由),就得和框架约定硬刚,改起来束手束脚。

所以我的结论是:适合中小型项目,或者团队不想花时间讨论规范的场景。但长期演进?有点僵。

“功能内聚”派:按模块划分目录

这个方案我比较喜欢。核心思想是:**一个业务功能的所有代码,放在同一个目录下**。比如:

src/
├── features/
│   ├── user-profile/
│   │   ├── components/
│   │   ├── hooks/
│   │   ├── types.ts
│   │   ├── utils.ts
│   │   └── index.tsx
│   └── checkout/
│       ├── components/
│       └── ...
├── shared/            # 公共部分
│   ├── ui/
│   └── lib/
└── app/               # 顶层入口

这种结构的最大优势是高内聚、低耦合。当你需要改用户资料页,所有相关代码就在 user-profile/ 里,不用满项目找文件。删功能也简单,直接删整个目录,基本不会漏依赖。

我拿它做过两个中型项目,体验非常爽。尤其是配合 TypeScript 的 path mapping,导入路径干净得一批:

// tsconfig.json
{
  "compilerOptions": {
    "paths": {
      "@features/*": ["src/features/*"],
      "@shared/*": ["src/shared/*"]
    }
  }
}
// 在组件中
import { useUserProfile } from '@features/user-profile/hooks';

不过也有坑。一是初期搭建成本高,得配好 ESLint、Prettier、TS path;二是如果模块划分不合理(比如把太小的功能单独成模块),反而造成目录爆炸。我第一次用时就犯了这错,搞出一堆只有两三个文件的目录,后来合并了一波才清爽。

但总体来说,只要团队有基本的模块拆分意识,这方案长期维护成本最低。

“传统分层”派:components / pages / utils 分家

这是最老派的做法,很多 React 教程还在推:

src/
├── components/
├── pages/
├── hooks/
├── utils/
├── types/
└── services/

乍一看很清晰,但实际开发中问题一大堆。比如一个“商品列表”功能,它的组件在 components/,数据请求在 services/,状态逻辑在 hooks/,类型定义在 types/……改个需求要横跨四个目录,Git diff 都看得眼花。

我去年帮朋友重构一个项目,就是这种结构。光是把“购物车”相关代码从四处八方聚拢到一起,就花了两天。而且这种结构天然鼓励写“全局 utils”——结果 utils/ 里堆满了只被一个地方调用的函数,根本不是“公共”。

除非是超小型 demo 或者一次性项目,否则我<strong绝不推荐**这种分法。它看起来规整,实则制造了大量隐性耦合和查找成本。

我的选型逻辑:看团队,看项目周期

现在我接新项目,第一件事就是问:这项目要做多久?团队多少人?有没有新人?

  • 如果是 1-2 人、3 个月内上线的小项目,我会直接用 Next.js 默认结构,省事。反正代码量不大,乱也乱不到哪去。
  • 如果是 3 人以上、预计维护半年以上,我一定推“功能内聚”模式。哪怕前期多花一天搭脚手架,后期能省十天排查时间。
  • 至于“传统分层”?除非客户死活不让改,否则我直接 pass。

这里有个细节:很多人担心“功能内聚”会导致重复代码。其实只要把真正通用的部分(比如 Button、Modal、request 封装)抽到 shared/,其他都按业务聚,重复率并不高。我亲测有效。

踩坑提醒:这三点一定注意

不管你选哪种,下面三个坑我踩过不止一次:

  1. 别让目录名暴露技术细节。比如 redux/zustand/。万一以后换状态管理,目录名全得改。用 state/ 或直接放模块内部更安全。
  2. 避免过深嵌套。超过三层的目录(比如 features/a/b/c/)基本说明模块拆太细。合并!
  3. 统一命名风格。要么全用 kebab-case(user-profile),要么全用 camelCase(userProfile)。混用的话,Linux 服务器上可能大小写不敏感,本地开发却报错,巨坑。

最后说句实在话

目录规范没有银弹。我见过团队用“传统分层”跑得飞起,也见过“功能内聚”被滥用成屎山。关键不是选哪个方案,而是团队达成共识,并严格执行。哪怕你选了个次优方案,只要所有人都遵守,也比三天一换强。

以上是我个人对目录规范的完整踩坑总结,有更优的实现方式欢迎评论区交流。如果你也在纠结这事,不妨先拿个小功能试两周,哪种让你少骂娘,就选哪种。

本文章不代表JZTHEME立场,仅为作者个人观点 / 研究心得 / 经验分享,旨在交流探讨,供读者参考。
发表评论

暂无评论