React 大型应用架构设计
大型 React 应用设计原则
1. Feature-Sliced Design 架构
src/
app/ # 应用层:路由、Provider、全局样式
pages/ # 页面层:组合 widgets 组成页面
widgets/ # 组件层:独立的页面区块(Header、Sidebar)
features/ # 功能层:用户操作(点赞、搜索、评论)
entities/ # 实体层:业务对象(User、Article、Order)
shared/ # 共享层:UI 组件库、工具函数、API 客户端
依赖规则:上层可引用下层,同层之间不能随意引用。
模块边界:每个 slice 只通过 index.ts 暴露公开 API。
2. 状态分层管理
服务端状态(Server State)
-> 来自 API,有缓存、失效、同步问题
-> React Query / SWR / RTK Query
客户端状态(Client State)
-> UI 状态(弹窗开关、选中项)
-> useState / useReducer / Zustand
表单状态(Form State)
-> 表单值、验证、提交
-> React Hook Form + Zod
URL 状态(URL State)
-> 搜索参数、分页、筛选条件
-> useSearchParams / nuqs 库
关键原则:不要把服务端状态放进 Redux/Zustand,React Query 专门处理异步状态。
3. 代码分割策略
// 路由级分割(必做)
const Dashboard = lazy(() => import('./pages/Dashboard'))
const Analytics = lazy(() => import('./pages/Analytics'))
// 功能级分割(大功能模块)
const RichEditor = lazy(() => import('./features/editor/RichEditor'))
// 预加载策略(hover 时触发)
const prefetchDashboard = () => import('./pages/Dashboard')
<Link
to="/dashboard"
onMouseEnter={prefetchDashboard} // 鼠标悬停时预加载
>
Dashboard
</Link>
4. 组件设计模式
Compound Components(复合组件)
// 用于构建高度可定制的组件
const Select = ({ children, value, onChange }) => {
return (
<SelectContext.Provider value={{ value, onChange }}>
<div className="select">{children}</div>
</SelectContext.Provider>
)
}
Select.Option = ({ value, children }) => {
const { value: selected, onChange } = useSelectContext()
return (
<div
className={selected === value ? 'selected' : ''}
onClick={() => onChange(value)}
>
{children}
</div>
)
}
// 使用
<Select value={val} onChange={setVal}>
<Select.Option value="a">Option A</Select.Option>
<Select.Option value="b">Option B</Select.Option>
</Select>
Render Props / Children as Function
// 将渲染逻辑控制权交给使用者
function DataProvider({ render }) {
const [data] = useFetch('/api/data')
return render(data)
}
// 使用:自定义渲染
<DataProvider render={data => <CustomChart data={data} />} />
useImperativeHandle 暴露命令式接口
const Dialog = forwardRef((props, ref) => {
const [open, setOpen] = useState(false)
useImperativeHandle(ref, () => ({
open: () => setOpen(true),
close: () => setOpen(false),
}))
return open ? <div>{props.children}</div> : null
})
// 使用
const dialogRef = useRef(null)
<Dialog ref={dialogRef} />
<button onClick={() => dialogRef.current.open()}>打开</button>
5. Monorepo 组织
packages/
ui/ # 共享 UI 组件库(Design System)
utils/ # 工具函数
types/ # 共享 TypeScript 类型
api-client/ # API 客户端(自动生成)
config/ # 共享配置(ESLint、TS、Tailwind)
apps/
web/ # 主应用
admin/ # 管理后台
mobile/ # React Native 应用
工具链:Turborepo(增量构建缓存)+ pnpm workspaces
版本管理:内部包用 workspace:*,公共包用 Changesets
6. 测试体系
// 单元测试:纯函数、hooks、工具
test('useCounter', () => {
const { result } = renderHook(() => useCounter(0))
act(() => result.current.increment())
expect(result.current.count).toBe(1)
})
// 集成测试:组件 + API Mock(MSW)
test('登录成功后跳转首页', async () => {
server.use(
http.post('/api/login', () => HttpResponse.json({ token: 'abc' }))
)
render(<LoginPage />, { wrapper: Providers })
await userEvent.type(screen.getByLabelText('邮箱'), 'a@b.com')
await userEvent.click(screen.getByRole('button', { name: '登录' }))
await expect(screen.findByText('欢迎回来')).resolves.toBeInTheDocument()
})
// E2E(Playwright):核心用户旅程
test('完整购买流程', async ({ page }) => {
await page.goto('/products')
await page.click('[data-testid=add-to-cart]')
await page.goto('/checkout')
await page.fill('[name=card]', '4242 4242 4242 4242')
await page.click('button[type=submit]')
await expect(page.getByText('订单已确认')).toBeVisible()
})
7. 资深面试题
- 如何设计一个跨团队复用的组件库?
- 严格的语义化版本 + Changesets 管理
- Storybook 文档 + 视觉回归测试(Chromatic)
- Compound Component 模式提供灵活性
- 组件 API 向后兼容,breaking change 走大版本
- 微前端架构中 React 应用如何共享状态?
- CustomEvent / window.postMessage(松耦合)
- Module Federation 共享单例 store
- URL 作为通信载体(筛选条件、路由参数)
- 大型 React 应用如何做权限控制?
- 路由级:
<PrivateRoute>包裹需鉴权路由 - 组件级:
usePermission('edit:post')hook - 数据级:服务端返回时过滤,前端只做展示判断
- Feature Flag:LaunchDarkly 实现灰度和 A/B 测试
- 路由级: