如何做大型React+RN 项目架构设计、目录规范

如果是大型 React + React Native 项目,我比较推荐的思路不是"把 Web 和 RN 的目录完全统一",而是:

业务架构统一、基础设施复用、平台实现隔离。

尤其当项目规模达到几十人、几十个业务模块、Web + iOS + Android 同时迭代时,最容易出问题的是:components/ 越堆越大、utils/ 变成垃圾场、Redux 全局状态失控、Web/RN 互相污染。

下面给你一套比较适合长期演进的架构。


一、整体架构

推荐:

vbnet 复制代码
                    ┌─────────────────────┐
                    │      Product        │
                    │     Business        │
                    └──────────┬──────────┘
                               │
                    ┌──────────▼──────────┐
                    │     Features        │
                    │ 业务功能 / Use Case   │
                    └──────────┬──────────┘
                               │
             ┌─────────────────┼─────────────────┐
             │                 │                 │
      ┌──────▼──────┐   ┌──────▼──────┐   ┌──────▼──────┐
      │   Web App   │   │   RN App    │   │ Shared UI   │
      │   React     │   │ iOS/Android │   │ Design Sys  │
      └──────┬──────┘   └──────┬──────┘   └─────────────┘
             │                 │
             └────────┬────────┘
                      │
              ┌───────▼────────┐
              │  Shared Domain │
              │ API / Model    │
              │ State / Utils  │
              └───────┬────────┘
                      │
              ┌───────▼────────┐
              │ Infrastructure │
              │ Network / Auth │
              │ Storage / Log  │
              └────────────────┘

如果是新项目,我会优先考虑:

Monorepo + Turborepo/Nx + pnpm + TypeScript

例如:

lua 复制代码
my-project/
├── apps/
│   ├── web/
│   └── mobile/
│
├── packages/
│   ├── api/
│   ├── domain/
│   ├── features/
│   ├── ui/
│   ├── hooks/
│   ├── utils/
│   ├── config/
│   └── types/
│
├── tooling/
│   ├── eslint/
│   ├── prettier/
│   ├── typescript/
│   └── jest/
│
├── package.json
├── pnpm-workspace.yaml
├── turbo.json
└── tsconfig.json

二、最重要的原则:按「业务」组织,而不是按「技术类型」

小项目经常这么写:

css 复制代码
src/
├── components/
├── pages/
├── hooks/
├── services/
├── stores/
├── utils/
└── types/

项目刚开始很好用。

但是大型项目最后会变成:

erlang 复制代码
components/
  Button.tsx
  UserCard.tsx
  ProductCard.tsx
  OrderCard.tsx
  xxxModal.tsx
  xxxModal2.tsx
  xxxModal3.tsx
  ...

hooks/
  useUser.ts
  useOrder.ts
  useProduct.ts
  useXXX.ts
  ...

services/
  user.ts
  order.ts
  product.ts
  ...

问题是业务边界消失了。

所以大型项目建议:

sql 复制代码
features/
├── auth/
├── user/
├── order/
├── product/
├── payment/
└── notification/

每个 Feature 自己管理:

bash 复制代码
features/order/
├── api/
├── components/
├── hooks/
├── model/
├── screens/
├── types.ts
└── index.ts

这样一个开发者负责 Order 模块时,大部分代码都在:

bash 复制代码
features/order/

而不是满项目搜索。


三、我比较推荐的最终目录

一个中大型 React + RN Monorepo 可以这样:

css 复制代码
project/
│
├── apps/
│   │
│   ├── web/
│   │   ├── src/
│   │   │   ├── app/
│   │   │   ├── pages/
│   │   │   ├── layouts/
│   │   │   ├── routes/
│   │   │   └── main.tsx
│   │   │
│   │   └── package.json
│   │
│   └── mobile/
│       ├── src/
│       │   ├── app/
│       │   ├── navigation/
│       │   ├── screens/
│       │   └── native/
│       │
│       └── package.json
│
├── packages/
│   │
│   ├── features/
│   │   ├── auth/
│   │   ├── user/
│   │   ├── order/
│   │   ├── product/
│   │   └── payment/
│   │
│   ├── domain/
│   │   ├── user/
│   │   ├── order/
│   │   └── product/
│   │
│   ├── api/
│   │   ├── client/
│   │   ├── auth/
│   │   ├── user/
│   │   ├── order/
│   │   └── product/
│   │
│   ├── ui/
│   │   ├── Button/
│   │   ├── Input/
│   │   ├── Modal/
│   │   ├── List/
│   │   └── Typography/
│   │
│   ├── hooks/
│   ├── utils/
│   ├── types/
│   ├── config/
│   └── i18n/
│
├── tooling/
├── package.json
├── pnpm-workspace.yaml
└── turbo.json

但这里还有一个关键问题:

featuresdomainui 到底怎么依赖?

这才是大型项目真正的架构核心。


四、建立严格的依赖方向

推荐把依赖关系控制成:

复制代码
App
 ↓
Feature
 ↓
Domain
 ↓
Infrastructure

同时:

复制代码
UI
 ↑
Feature

更完整一点:

markdown 复制代码
                 ┌─────────────┐
                 │    apps     │
                 │ Web / Mobile│
                 └──────┬──────┘
                        ↓
                 ┌─────────────┐
                 │  features   │
                 │ 业务用例     │
                 └──────┬──────┘
                        ↓
              ┌─────────┴─────────┐
              ↓                   ↓
        ┌──────────┐        ┌──────────┐
        │  domain  │        │    ui    │
        └────┬─────┘        └──────────┘
             ↓
        ┌──────────┐
        │   api    │
        └──────────┘

例如:

bash 复制代码
features/order
    ↓
domain/order
    ↓
api/order

而不要:

bash 复制代码
api/order
    ↓
features/order

否则很容易形成循环依赖。


五、Feature 内部怎么设计

例如订单模块:

css 复制代码
features/order/
│
├── api/
│   ├── getOrders.ts
│   ├── getOrderDetail.ts
│   ├── createOrder.ts
│   └── cancelOrder.ts
│
├── components/
│   ├── OrderCard/
│   ├── OrderStatus/
│   └── OrderPrice/
│
├── hooks/
│   ├── useOrders.ts
│   └── useOrderDetail.ts
│
├── model/
│   ├── order.store.ts
│   ├── order.selectors.ts
│   └── order.mapper.ts
│
├── screens/
│   ├── OrderList/
│   └── OrderDetail/
│
├── types.ts
└── index.ts

这样:

css 复制代码
Order
├── API
├── UI
├── State
├── Hook
├── Screen
└── Type

全部聚合在一个地方。


六、React Web 和 RN 怎么复用?

这是 React + RN 项目最关键的问题之一。

不要追求:

Web 和 RN 100% 共用组件。

应该追求:

业务逻辑最大化复用,UI 根据平台适配。

例如:

css 复制代码
packages/
├── features/
│   └── order/
│       ├── hooks/
│       ├── model/
│       ├── api/
│       └── components/
│
└── ui/

业务逻辑:

scss 复制代码
useOrder()
useOrderDetail()
useCancelOrder()

可以共用。

但 UI:

java 复制代码
OrderCard.web.tsx
OrderCard.native.tsx

可以分别实现。

例如:

java 复制代码
OrderCard/
├── OrderCard.tsx
├── OrderCard.web.tsx
├── OrderCard.native.tsx
├── OrderCard.types.ts
└── index.ts

或者:

java 复制代码
OrderCard/
├── index.ts
├── OrderCard.web.tsx
└── OrderCard.native.tsx

Webpack / Metro 根据平台自动选择。


七、不要把「业务组件」和「基础 UI」混在一起

这是大型项目非常重要的一条规则。

例如:

bash 复制代码
packages/ui/

应该放:

css 复制代码
Button
Input
Text
Icon
Modal
Dialog
Avatar
Card
Tabs
List
Spinner

而不要放:

复制代码
UserCard
OrderCard
ProductCard
PaymentForm
OrderStatus

后面这些属于:

bash 复制代码
features/user
features/order
features/product
features/payment

也就是:

UI Component

css 复制代码
Button
Modal
Input
Avatar

Business Component

复制代码
OrderCard
OrderStatus
ProductSelector
PaymentForm

这两个层级一定要分开。


八、State Management 不要一个 Redux 打天下

大型项目建议把状态分成四种:

arduino 复制代码
                    State
                      │
        ┌─────────────┼─────────────┐
        ↓             ↓             ↓
   Server State   Client State   Local State
        │             │             │
   TanStack Query   Zustand       useState

1. Server State

例如:

复制代码
用户信息
订单列表
商品列表
消息列表

推荐:

复制代码
TanStack Query

例如:

php 复制代码
const { data } = useQuery({
  queryKey: ['orders'],
  queryFn: getOrders,
})

不要把 API 返回的数据全部塞进 Redux。


2. Client State

例如:

复制代码
当前主题
登录状态
购物车 UI 状态
侧边栏状态
用户偏好

可以:

复制代码
Zustand

或者 Redux Toolkit。


3. Local State

例如:

css 复制代码
Modal 是否打开
Input 当前值
Tab 当前 index
Dropdown 是否展开

直接:

scss 复制代码
useState()

不要为了:

复制代码
isModalOpen

创建一个 global store。


4. URL State

Web 特别需要考虑:

ini 复制代码
?page=2
&sort=price
&category=phone
&keyword=iphone

这种状态应该属于 URL,而不是 Redux。


九、API 层不要到处直接 axios/fetch

错误:

scss 复制代码
function OrderList() {
  const [data, setData] = useState()

  useEffect(() => {
    fetch('/api/orders')
  }, [])
}

大型项目应该:

arduino 复制代码
API Client
    ↓
API Service
    ↓
Feature Hook
    ↓
Component

例如:

sql 复制代码
packages/api/
├── client/
│   ├── http.ts
│   ├── interceptors.ts
│   └── error.ts
│
├── order/
│   ├── getOrders.ts
│   ├── getOrderDetail.ts
│   └── createOrder.ts
│
└── user/
    └── getUser.ts

然后:

csharp 复制代码
export async function getOrders() {
  return api.get<Order[]>('/orders')
}

Feature:

php 复制代码
export function useOrders() {
  return useQuery({
    queryKey: ['orders'],
    queryFn: getOrders,
  })
}

UI:

csharp 复制代码
function OrderList() {
  const { data } = useOrders()

  return ...
}

这样 UI 不知道:

css 复制代码
axios
fetch
HTTP Header
Token
Retry
Error Code

十、Domain 层什么时候需要?

如果项目很大,我非常建议加:

bash 复制代码
packages/domain/

比如:

bash 复制代码
domain/order/
├── entities.ts
├── types.ts
├── rules.ts
├── calculations.ts
└── validators.ts

例如:

vbnet 复制代码
export function canCancelOrder(order: Order) {
  return (
    order.status === 'PENDING' ||
    order.status === 'PROCESSING'
  )
}

这属于业务规则。

不要写进:

xml 复制代码
<OrderButton>

否则业务规则会散落在几十个组件里。


十一、复杂业务建议进一步采用 Clean Architecture

如果是非常大型的系统,比如:

  • 电商
  • 金融
  • SaaS
  • CRM
  • ERP
  • 即时通讯
  • 超大型 App

可以进一步:

markdown 复制代码
Presentation
      ↓
Application
      ↓
Domain
      ↓
Infrastructure

例如:

css 复制代码
order/
├── presentation/
│   ├── components/
│   ├── screens/
│   └── hooks/
│
├── application/
│   ├── useCases/
│   │   ├── createOrder.ts
│   │   ├── cancelOrder.ts
│   │   └── payOrder.ts
│   └── services/
│
├── domain/
│   ├── entities/
│   ├── valueObjects/
│   ├── rules/
│   └── repositories/
│
└── infrastructure/
    ├── api/
    ├── repository/
    └── storage/

不过不要一上来就 Clean Architecture 全套

很多 React 项目最后会变成:

复制代码
OrderEntity
OrderRepository
OrderRepositoryImpl
OrderUseCase
OrderService
OrderServiceImpl
OrderMapper
OrderDTO
OrderVO

一个简单的:

scss 复制代码
cancelOrder()

被搞成十几个文件。

架构不是层数越多越高级。


十二、Web / RN 平台代码怎么隔离

推荐:

css 复制代码
packages/
├── ui/
│   ├── Button/
│   │   ├── Button.tsx
│   │   ├── Button.web.tsx
│   │   └── Button.native.tsx
│
├── platform/
│   ├── storage/
│   │   ├── index.ts
│   │   ├── storage.web.ts
│   │   └── storage.native.ts
│   │
│   ├── navigation/
│   │   ├── index.ts
│   │   ├── navigation.web.ts
│   │   └── navigation.native.ts
│   │
│   └── notification/
│       ├── index.ts
│       ├── notification.web.ts
│       └── notification.native.ts

业务层只使用:

javascript 复制代码
import { storage } from '@repo/platform/storage'

而不是:

ini 复制代码
if (Platform.OS === 'ios') ...
if (Platform.OS === 'android') ...
if (isWeb) ...

到处写平台判断。


十三、RN 特别容易踩的坑

RN 项目不要让业务代码直接依赖:

复制代码
AsyncStorage
MMKV
PushNotification
DeviceInfo
React Navigation
NativeModules

建议包一层:

bash 复制代码
packages/platform/
├── storage/
├── device/
├── notification/
├── analytics/
├── permission/
└── navigation/

例如:

javascript 复制代码
// 不推荐
import AsyncStorage from '@react-native-async-storage/async-storage'

业务层:

csharp 复制代码
// 推荐
import { storage } from '@repo/platform/storage'

await storage.set('token', token)

以后从:

复制代码
AsyncStorage

换成:

复制代码
MMKV

业务代码完全不用改。


十四、权限和登录也要独立出来

大型项目通常需要:

sql 复制代码
auth/
├── session/
├── token/
├── permission/
├── user/
└── guards/

例如:

xml 复制代码
<RequirePermission permission="order:create">
  <CreateOrder />
</RequirePermission>

不要到处:

ini 复制代码
if (user.role === 'admin') {
  ...
}

否则权限规则很快失控。


十五、目录规范建议制定成「规则」,而不是建议

比如团队直接规定:

Rule 1

禁止:

bash 复制代码
features/order → features/payment

如果确实需要共享:

bash 复制代码
domain/payment

或者:

vbnet 复制代码
shared/

Rule 2

禁止业务代码直接访问基础设施:

复制代码
features/* → axios
features/* → AsyncStorage
features/* → MMKV

必须:

bash 复制代码
features
 ↓
api/platform

Rule 3

utils/ 禁止无限增长。

禁止:

css 复制代码
utils/
├── user.ts
├── order.ts
├── payment.ts

如果是业务逻辑:

bash 复制代码
features/user
features/order
features/payment

如果是通用工具:

lua 复制代码
utils/
├── date.ts
├── number.ts
├── string.ts

十六、推荐用 ESLint 强制架构

这一点非常重要。

不要只靠 Code Review。

例如规定:

bash 复制代码
apps/web
    ↓
packages/features
    ↓
packages/domain
    ↓
packages/api

用 ESLint / dependency-cruiser / Nx module boundaries 等工具限制依赖。

最终让错误直接报:

arduino 复制代码
❌ features/order cannot import features/payment
❌ domain cannot import React
❌ domain cannot import React Native
❌ shared cannot import business modules

这样架构才是真正落地。


十七、命名规范

推荐统一:

scss 复制代码
组件:PascalCase

OrderCard/
UserProfile/

Hook:useXxx

useOrder()
useUser()
usePayment()

Store:
order.store.ts
auth.store.ts

API:
getOrders.ts
createOrder.ts
cancelOrder.ts

Types:
types.ts

工具:
formatDate.ts
parsePrice.ts

尽量不要:

复制代码
common.ts
helper.ts
utils.ts
misc.ts
tools.ts

这种名字在大型项目中很容易变成垃圾桶。


十八、一个比较成熟的完整版本

如果让我直接给一个50~100 人团队可以长期使用的 React + RN 架构,我会倾向:

css 复制代码
repo/
│
├── apps/
│   ├── web/
│   │   ├── src/
│   │   │   ├── app/
│   │   │   ├── routes/
│   │   │   ├── layouts/
│   │   │   └── pages/
│   │   └── package.json
│   │
│   └── mobile/
│       ├── src/
│       │   ├── app/
│       │   ├── navigation/
│       │   └── screens/
│       └── package.json
│
├── packages/
│   │
│   ├── features/
│   │   ├── auth/
│   │   ├── user/
│   │   ├── product/
│   │   ├── order/
│   │   └── payment/
│   │
│   ├── domain/
│   │   ├── user/
│   │   ├── product/
│   │   ├── order/
│   │   └── payment/
│   │
│   ├── api/
│   │   ├── client/
│   │   ├── auth/
│   │   ├── user/
│   │   ├── product/
│   │   └── order/
│   │
│   ├── ui/
│   │   ├── Button/
│   │   ├── Input/
│   │   ├── Modal/
│   │   ├── Card/
│   │   └── List/
│   │
│   ├── platform/
│   │   ├── storage/
│   │   ├── notification/
│   │   ├── analytics/
│   │   ├── device/
│   │   └── permission/
│   │
│   ├── state/
│   ├── hooks/
│   ├── utils/
│   ├── types/
│   ├── config/
│   ├── i18n/
│   └── constants/
│
├── tooling/
│   ├── eslint/
│   ├── prettier/
│   ├── typescript/
│   └── jest/
│
├── docs/
│   ├── architecture/
│   ├── conventions/
│   └── adr/
│
├── package.json
├── pnpm-workspace.yaml
├── turbo.json
└── tsconfig.json

十九、最终可以记住这张图

大型 React + RN 项目,我建议最终形成:

sql 复制代码
                 Web App       RN App
                    │             │
                    └──────┬──────┘
                           ↓
                    ┌─────────────┐
                    │   Feature   │
                    │             │
                    │ User        │
                    │ Order       │
                    │ Product     │
                    │ Payment     │
                    └──────┬──────┘
                           ↓
              ┌────────────┴────────────┐
              ↓                         ↓
       ┌────────────┐             ┌───────────┐
       │   Domain   │             │    UI     │
       │ Business   │             │ Design    │
       │ Rules      │             │ System    │
       └─────┬──────┘             └───────────┘
             ↓
       ┌────────────┐
       │    API     │
       └─────┬──────┘
             ↓
       ┌────────────┐
       │Infrastructure│
       │ Network      │
       │ Storage      │
       │ Native       │
       └─────────────┘

一句话总结:

Monorepo 管工程,Feature 管业务,Domain 管规则,API 管数据,UI 管表现,Platform 管 Web/RN 差异,ESLint 管依赖边界。

如果你现在正准备从 0 搭建这种项目,我还可以进一步给你一份**"React + RN + TypeScript + pnpm + Turborepo + TanStack Query + Zustand"的可直接落地目录、package.json、tsconfig、ESLint、路径别名以及一个完整 Order 模块示例**,会比单纯目录规范更接近真实生产项目。

我也可以给你画一张React + RN 大型项目架构图,把 App、Feature、Domain、API、UI、Native Platform 之间的依赖关系直观展示出来。

相关推荐
一位正在转型AI全栈的前端工程师1 小时前
AI 全栈学习之旅 -Week 8:从单 Agent 到多 Agent 协作:LangGraph 实战与记忆持久化
前端·python
用户921080262861 小时前
Promise 和 async/await:从用途到执行机制
前端
yyt3630458411 小时前
KLineChartQuant:GLSL 的精度问题及 RTC 解法
前端·vue.js·react.js·交互·图形渲染·webgl·数据可视化
HarmonLTS2 小时前
智盾 WAF v8.2 Ultra|下一代 Web 应用防火墙
前端
এ慕ོ冬℘゜2 小时前
前端实战:基于jQuery递归实现通用树形组织菜单(可直接复用)
前端·javascript·jquery
计算机魔术师2 小时前
英伟达据报洽谈向 Mira Murati 的 Thinking Machines Lab 投资 25 亿美元
前端
变与不变8062 小时前
js函数与封装详细解答
前端·javascript·vue.js
南雨北斗2 小时前
Vue 3 项目中的拦截器的作用和写法(动态设置响应头适配表单文件上传)
前端
涛涛ing3 小时前
125秒到10秒:TypeScript 7.0用Go重写编译器,前端圈等了14年
前端