如果是大型 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
但这里还有一个关键问题:
features、domain、ui 到底怎么依赖?
这才是大型项目真正的架构核心。
四、建立严格的依赖方向
推荐把依赖关系控制成:
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 之间的依赖关系直观展示出来。