React 大型项目目录骨架

98 阅读4分钟

React 大型项目目录骨架

前置:React 数据请求:TanStack Query
目标:建立可扩展的目录直觉:features / pages / shared,知道新代码该往哪放。

引言

入门 demo 往往是:

src/
  App.tsx
  components/
    Header.tsx
    TodoList.tsx
    UserCard.tsx
    ...

项目变大以后,问题不是「React 不会写」,而是:

  • 改一个业务,要在 components/ 里翻半天才找到相关文件
  • 首页、登录、任务模块的组件混在一起,互相乱引用
  • 新人不知道「这个按钮 / 这个 hook 该放哪」

本篇不追完美架构,只给一套够用、可长的起步骨架:按 业务(feature) 内聚,页面保持薄,跨业务的东西进 shared。

本篇 demo/ 已按此拆了一版可点的迷你应用:首页 + 任务模块 + 共享按钮。

目录示意

大型 React 项目目录

推荐起步结构:

src/
  app/          # 可选:全局 provider、路由表
  pages/        # 路由级页面(薄)
  features/     # 按业务内聚(tasks、auth…)
  shared/       # 跨业务 UI / utils

动手

完整代码:react2/08-large-project/demo

cd react2/08-large-project/demo
npm install
npm run dev
  • / → pages/HomePage
  • /tasks → features/tasks/TasksPage(用 shared/Button)

点顶栏「首页 / 任务」,感受「路由页」和「业务模块」是怎么分开的。


1. 为什么不要一股脑塞进 components/

做法短期项目长大以后
全部丢 components/好找文件名业务边界糊掉,互相 import 成蜘蛛网
按 UI 类型分(buttons/、forms/)看起来整齐改「任务」仍要跨很多目录
按 feature 分(features/tasks/)多建一层目录相关代码挨在一起,改一块少翻别处

直觉:经常一起改的,放一起。
「任务列表 + 加任务逻辑 + 任务相关 hook」属于 features/tasks;「全站都能用的按钮」才进 shared。


2. 四层分别干什么

pages/:路由级页面(尽量薄)

对应 URL 入口,少写业务细节,多做组装:

// pages/HomePage.tsx
import { Link } from 'react-router-dom'

export function HomePage() {
  return (
    <section className="panel">
      <h1>首页</h1>
      <p>
        大型项目按 <code>features / pages / shared</code> 拆目录,功能内聚、页面薄。
      </p>
      <Link to="/tasks">进入任务模块 →</Link>
    </section>
  )
}

约定:

  • 一个路由 ≈ 一个 page(或 page 再调 feature)
  • page 里少堆 useState / 复杂请求;重逻辑下沉到 features/

features/:按业务内聚

一个业务一块地:页面、组件、hooks、API 都可以放在同一 feature 下。

// features/tasks/TasksPage.tsx
import { useState } from 'react'
import { Button } from '../../shared/Button'

export function TasksPage() {
  const [items, setItems] = useState(['搭建目录', '拆分 feature', '接入路由'])

  return (
    <section className="panel">
      <h1>任务 Feature</h1>
      <ul>
        {items.map((item) => (
          <li key={item}>{item}</li>
        ))}
      </ul>
      <Button
        onClick={() =>
          setItems((list) => [...list, `新任务 ${list.length + 1}`])
        }
      >
        添加任务
      </Button>
    </section>
  )
}

长大以后可以继续往下拆,例如:

features/tasks/
  TasksPage.tsx
  components/TaskItem.tsx
  hooks/useTasks.ts
  api.ts

先别过度拆;文件变多、找起来费劲时再拆。

shared/:跨业务复用

只放「两个及以上 feature 都会用」的东西:通用 Button、布局、日期格式化、fetch 封装等。

// shared/Button.tsx
import type { ButtonHTMLAttributes, ReactNode } from 'react'

type Props = ButtonHTMLAttributes<HTMLButtonElement> & {
  children: ReactNode
}

export function Button({ children, ...rest }: Props) {
  return (
    <button type="button" className="btn" {...rest}>
      {children}
    </button>
  )
}

判断口诀:

  • 只有任务模块用 → 留在 features/tasks
  • 首页、任务、设置都会用 → shared

app/(可选):全局装配

Provider(QueryClient、主题)、路由表、鉴权壳子等「应用级胶水」可以放这里。
本 demo 体量小,路由直接写在 App.tsx,没有单独建 app/,这是刻意简化。


3. 路由怎么把它们串起来

// App.tsx
import { BrowserRouter, Link, Route, Routes } from 'react-router-dom'
import { TasksPage } from './features/tasks/TasksPage'
import { HomePage } from './pages/HomePage'

export default function App() {
  return (
    <BrowserRouter>
      <div className="shell">
        <header className="top">
          <strong>LargeApp 骨架</strong>
          <nav>
            <Link to="/">首页</Link>
            <Link to="/tasks">任务</Link>
          </nav>
        </header>
        <main>
          <Routes>
            <Route path="/" element={<HomePage />} />
            <Route path="/tasks" element={<TasksPage />} />
          </Routes>
        </main>
      </div>
    </BrowserRouter>
  )
}

依赖方向可以记成:

App(路由 / 壳)
  → pages / features(页面与业务)
    → shared(通用 UI / utils)

尽量避免:shared 去 import 某个 features/xxx(通用层不应依赖具体业务)。


4. 对照表

目录回答的问题demo 里有什么
pages/这个 URL 入口是谁?HomePage
features/tasks/任务业务的代码在哪?TasksPage + 本地 state
shared/全站通用控件?Button
App.tsx壳子 + 路由表顶栏 + /、/tasks
新增需求优先落点
新路由「关于页」pages/AboutPage + 挂一条 Route
任务详情、任务筛选features/tasks/ 里加文件
全站统一的 Inputshared/Input.tsx
登录 / 权限一整块features/auth/(不要散到各处)

5. 常见坑

做法问题
一切仍丢 components/目录「看起来有结构」,业务边界没有
page 里写满请求与业务规则页面变胖,难复用、难测
feature 之间互相乱引用tasks ↔ auth 缠死,拆不动
过早建 entities / widgets 七层小项目被架构压垮;先 features + shared 够用
把只属于任务的按钮放进 sharedshared 膨胀成杂物间

demo 在练什么

  1. 用 pages / features / shared 看清三层职责
  2. 路由只负责挂载,业务状态留在 feature 内
  3. feature 通过 shared/Button 复用通用 UI,而不是复制一份样式按钮

自己改一改:再加一个 features/notes/,挂 /notes;或把任务列表拆成 TaskItem.tsx 仍放在 features/tasks/ 下。


运行截图

大型项目骨架首页