React 的“主流库”不像表面上那么少。很多人一聊 React 生态,脑子里只有 React Router 和 Redux,但实际上常用的路由、状态管理、数据请求、表单、UI 组件、动画、跨端框架加起来至少有 10 个以上。这次我们把 React 生态里最常用的主流库全部过一遍,不做概念堆砌,直接看每个库负责什么、怎么选、怎么在项目里落地,以及最容易踩的坑。内容包括核心能力速览、工程化环境准备、路由实战、数据请求方案、状态管理选型、表单处理、组件库和动画方案,最后给出一套可复制的排查思路和使用建议。
如果你正在做 React 技术选型、准备上手 React 生态,或者想把老项目里的方案换掉,这篇文章可以直接收藏。
1. React 主流库核心能力速览
React 本身是视图层框架,真正让项目跑起来的业务能力,基本都来自周边库。按照用途划分,可以把 React 主流库分成下面几类:
| 能力方向 | 主流库/方案 | 主要解决的问题 | 核心特点 |
|---|---|---|---|
| 路由 | React Router | 页面切换、URL 与组件映射 | 声明式路由,支持嵌套路由、路由守卫、懒加载 |
| 状态管理 | Redux Toolkit、Zustand、Jotai | 跨组件共享状态、全局状态持久化 | Redux 适合大型项目,Zustand 轻量,Jotai 原子化 |
| 数据请求 | TanStack Query、Axios、SWR | 服务端状态、请求缓存、重试、分页 | Query 自动缓存/去重,适合接口密集型页面 |
| 表单 | React Hook Form、Formik | 表单状态、校验、错误处理 | RHF 性能好,配合 Zod 可做类型安全校验 |
| UI 组件库 | Ant Design、Material UI、Arco Design | 企业级后台组件、表单组件、表格 | 开箱即用,适合中后台快速开发 |
| 样式方案 | Tailwind CSS、CSS Modules、styled-components | 样式组织、主题切换、样式隔离 | Tailwind 原子类,CSS Modules 局部作用域 |
| 动画 | Framer Motion、React Spring | 页面转场、元素动画、手势交互 | 声明式 API,配合 React 状态驱动动画 |
| 框架 | Next.js、Remix | 全栈 React、SSR、路由、服务端数据获取 | 适合 SEO 场景和全栈项目 |
| 跨端 | React Native、Expo | 移动端 App | 一套代码运行 Android/iOS |
| 组件测试 | Jest、React Testing Library、Vitest | 组件单元测试、交互测试 | RTL 强调用户行为测试 |
从选型角度看,一套常见的中后台技术栈可能是:Vite + React Router + Zustand + TanStack Query + React Hook Form + Ant Design + Tailwind CSS。一套面向内容站点或全栈应用的技术栈可能是:Next.js + TanStack Query + Tailwind CSS + Framer Motion。面向移动端则是 React Native 或 Expo 起步。
这里要强调一个原则:没有“最好”的库,只有当前项目阶段最合适的组合。后面每个章节会说明不同方案的取舍和适用边界。
2. 适用场景与使用边界
React 主流库覆盖面很广,但不同场景的选型思路差别很大。先想清楚项目类型,再决定要不要引入某个库。
2.1 适合的场景
中后台管理端是 React 生态最强的场景。这类项目页面结构规整、表单和表格密集、路由层级固定,非常适合用 React Router + Ant Design + React Hook Form + TanStack Query 的组合。开发速度明显快于从零手写组件。
内容型网站和需要 SEO 的页面,应该直接考虑 Next.js。因为纯客户端渲染的 React 项目对搜索引擎不友好,而 Next.js 的服务端渲染和静态生成可以解决这个问题。
移动端开发则有 React Native 和 Expo 路线。如果团队已经熟悉 React,用 RN 做跨端方案,代码复用率比单独维护两套原生项目要高很多。
2.2 不适合的场景
不是所有项目都需要重量级状态管理库。如果页面状态基本是组件内部状态,用 Redux 反而增加代码量和心智负担。小型工具页、单页展示页,直接用 useState + useReducer 就足够了。
动画库也不是必须上。如果只是简单的 CSS 过渡,用 transition 和 animation 就可以。Framer Motion 适合复杂动画和交互动效,但不适合为了“炫技”而引入一个 50KB 以上的运行时依赖。
2.3 版权、安全与合规边界
React 本身是 MIT 协议,主流周边库大多也是 MIT 或 Apache 2.0 协议,可以用于商业项目。但要注意两点:第一,使用组件库和开源代码时保留原协议声明;第二,在项目里引入第三方接口和依赖时,不要随意绕过权限校验。涉及用户数据、登录态的页面,必须在服务端做鉴权,不能只靠前端路由守卫。
3. React 项目环境准备与工程初始化
主流库的落地,需要先有一个可运行的 React 工程。现在推荐用 Vite 来初始化,而不是老的 Create React App。
# 使用 Vite 创建 React + TypeScript 项目 npm create vite@latest my-react-app -- --template react-ts cd my-react-app npm install npm run dev环境要求并不复杂:Node.js 建议使用 18 以上版本,包管理器可以选择 npm、pnpm 或 yarn。pnpm 在依赖安装速度和磁盘占用上更优,适合 monorepo 和大型项目。
# 常用依赖安装命令,按项目需要选择 npm install react-router-dom npm install zustand npm install @tanstack/react-query npm install axios npm install react-hook-form zod npm install antd @ant-design/icons npm install tailwindcss @tailwindcss/vite npm install framer-motion建议工程结构按功能模块划分目录:
src/ api/ # 接口请求封装 components/ # 全局通用组件 features/ # 业务模块 hooks/ # 通用 Hooks layouts/ # 布局组件 router/ # 路由配置 stores/ # 状态管理 types/ # 类型定义 utils/ # 工具函数 App.tsx main.tsx这种结构的好处是路由、状态、接口层职责清晰,后面接入任何一个库都有明确的放置位置。初始化阶段最容易遇到的问题就是依赖版本冲突,所以建议使用 pnpm 锁定依赖版本,或者使用 package.json 里的 lock 文件统一版本。
4. 路由方案:React Router 实战
React Router 是目前 React 生态里使用率最高的路由库。日常开发中,主要涉及基础路由配置、嵌套路由、懒加载、路由守卫四类能力。
4.1 基础路由与嵌套路由
新版 React Router 使用createBrowserRouter来配置路由表。以下是一个中后台项目常用结构:
// router/index.tsx import { createBrowserRouter, Navigate } from 'react-router-dom' import Layout from '@/layouts/AdminLayout' import Dashboard from '@/features/dashboard' import UserList from '@/features/user/UserList' import UserDetail from '@/features/user/UserDetail' import Login from '@/features/login' const router = createBrowserRouter([ { path: '/login', element: <Login /> }, { path: '/', element: <Layout />, children: [ { index: true, element: <Navigate to="/dashboard" replace /> }, { path: 'dashboard', element: <Dashboard /> }, { path: 'user', children: [ { index: true, element: <UserList /> }, { path: ':id', element: <UserDetail /> } ] } ] } ]) export default router入口组件中配合RouterProvider加载路由:
// main.tsx import { RouterProvider } from 'react-router-dom' import router from './router' createRoot(document.getElementById('root')!).render( <RouterProvider router={router} /> )嵌套路由最直接的收益是父布局复用。侧边栏、头部导航共享一次,子页面只需要承担内容区渲染,代码可维护性好很多。
4.2 懒加载
大型项目路由按页面拆包,可以减少首屏体积。
import { lazy, Suspense } from 'react' const Dashboard = lazy(() => import('@/features/dashboard')) const UserList = lazy(() => import('@/features/user/UserList')) <Suspense fallback={<PageLoading />}> <Dashboard /> <UserList /> </Suspense>懒加载之后,每个路由页面的 JavaScript 会按访问顺序请求,而不是首屏一次全拉下来。首次访问某页面时会有短暂的加载态,需要准备统一的 Loading 组件。
4.3 路由守卫
React Router 没有内置“路由守卫”概念,但可以通过封装高阶组件或组件包裹实现。登录态判断是常见场景:
// components/AuthGuard.tsx import { Navigate, Outlet } from 'react-router-dom' import { useAuthStore } from '@/stores/auth' export default function AuthGuard() { const token = useAuthStore((state) => state.token) if (!token) { return <Navigate to="/login" replace/> } return <Outlet/> }然后在路由表中给需要权限的父路由包一层 AuthGuard:
{ path: '/', element: <AuthGuard />, children: [ { path: 'dashboard', element: <Dashboard /> } ] }需要明确的是,前端路由守卫只是用户体验层面的限制,真正的数据权限必须在接口服务端确认,否则绕过前端直接请求接口依然可能拿到数据。
5. 状态管理:Redux Toolkit、Zustand 与 Jotai
状态管理是 React 项目最容易过度设计的地方。这里按照项目规模给出三种方案的选型建议。
5.1 Redux Toolkit:大型中后台的默认选择
Redux Toolkit 是 Redux 官方推荐写法,内置了 createSlice、configureStore、createAsyncThunk,代码量比老 Redux 少很多。
// stores/user.ts import { createSlice, createAsyncThunk } from '@reduxjs/toolkit' import { fetchUserList } from '@/api/user' export const getUserList = createAsyncThunk( 'user/getList', async (params: ListParams) => { return await fetchUserList(params) } ) const userSlice = createSlice({ name: 'user', initialState: { list: [], loading: false }, reducers: { clearList(state) { state.list = [] } }, extraReducers(builder) { builder .addCase(getUserList.pending, (state) => { state.loading = true }) .addCase(getUserList.fulfilled, (state, action) => { state.loading = false state.list = action.payload }) } }) export const { clearList } = userSlice.actions export default userSlice.reducerRedux Toolkit 适合多模块共享状态、有复杂业务流转、需要时间旅行调试的场景。它的缺点是样板代码仍然偏多,小团队小项目会有“写半天状态、写半天页面”的感觉。
5.2 Zustand:轻量、直接、可替换
Zustand 是目前口碑较好的轻量状态库。API 非常精简,不需要 Provider 包裹,直接在组件外创建 store,组件里用 hook 消费:
// stores/useCartStore.ts import { create } from 'zustand' interface CartState { items: CartItem[] total: number addItem: (item: CartItem) => void removeItem: (id: string) => void } export const useCartStore = create<CartState>((set) => ({ items: [], total: 0, addItem: (item) => set((state) => ({ items: [...state.items, item], total: state.total + item.price })), removeItem: (id) => set((state) => ({ items: state.items.filter((item) => item.id !== id), total: state.total - (state.items.find((item) => item.id === id)?.price ?? 0) })) }))组件里使用:
const items = useCartStore((state) => state.items) const addItem = useCartStore((state) => state.addItem)Zustand 非常推荐用于电商购物车、主题切换、登录态、多步骤表单这类场景。它有浅比较机制,选择器的写法可以让组件避免不必要的渲染。Zustand 还支持持久化中间件,可以把 token 等状态存到 localStorage。
5.3 Jotai:原子化状态
Jotai 的设计思路是把状态拆成原子。如果说 Zustand 是把状态集中在一个 store,Jotai 就是一个一个独立的小状态。适合状态间依赖关系简单、希望按组件维度拆分状态的场景。
import { atom, useAtom } from 'jotai' const themeModeAtom = atom<'light' | 'dark'>('light') const userInfoAtom = atom<UserInfo | null>(null) function ThemeSwitch() { const [mode, setMode] = useAtom(themeModeAtom) return <button onClick={() => setMode(mode === 'light' ? 'dark' : 'light')} /> }Jotai 对 TypeScript 支持友好,状态定义和消费都在组件附近,适合中小项目或者想把状态“原子化”拆开的团队。
6. 数据请求方案:TanStack Query 与 Axios
大部分 React 项目的接口数据来自远端,这部分状态属于“服务端状态”,和组件状态、业务状态不是一回事。服务端状态管理的核心问题包括:缓存、Loading 态、错误重试、分页、增量更新。TanStack Query 就是解决这个问题的。
6.1 QueryClient 配置
先创建一个 QueryClient,并在应用根部包裹:
// main.tsx import { QueryClient, QueryClientProvider } from '@tanstack/react-query' const queryClient = new QueryClient({ defaultOptions: { queries: { staleTime: 60 * 1000, retry: 2 } } }) createRoot(document.getElementById('root')!).render( <QueryClientProvider client={queryClient}> <App /> </QueryClientProvider> )staleTime 决定了数据在多久内认为是新鲜的,过期的数据在组件挂载时后台重新请求。retry 控制失败后的重试次数。合理配置 staleTime 能显著减少无意义的重复请求。
6.2 useQuery 与 useMutation
列表页请求使用 useQuery:
// features/user/useUsers.ts import { useQuery } from '@tanstack/react-query' import { fetchUserList } from '@/api/user' export function useUsers(params: ListParams) { return useQuery({ queryKey: ['users', params], queryFn: () => fetchUserList(params), placeholderData: keepPreviousData }) }组件使用:
const { data, isLoading, isError, refetch } = useUsers({ page: 1, size: 20 })新增、编辑、删除这类写操作使用 useMutation:
import { useMutation, useQueryClient } from '@tanstack/react-query' const queryClient = useQueryClient() const createUserMutation = useMutation({ mutationFn: (payload: CreateUserPayload) => createUser(payload), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['users'] }) } })onSuccess 里 invalidateQueries 会让旧的列表缓存失效,并自动重新拉取。这是 TanStack Query 和手写请求逻辑最大的区别:写操作成功之后,数据可以自动同步。
6.3 Axios 请求封装
Axios 更多是作为请求执行器和拦截器使用。常见的做法是统一封装一个 request 实例:
// api/request.ts import axios from 'axios' import { message } from 'antd' const request = axios.create({ baseURL: import.meta.env.VITE_API_BASE_URL, timeout: 15000 }) request.interceptors.request.use((config) => { const token = localStorage.getItem('token') if (token) { config.headers.Authorization = `Bearer ${token}` } return config }) request.interceptors.response.use( (response) => response.data, (error) => { message.error(error.message || '请求失败') return Promise.reject(error) } ) export default request实际操作中,Axios 负责请求发送和错误处理,TanStack Query 负责缓存和状态管理。两者配合,代码结构很清晰。
7. 表单方案:React Hook Form + Zod
React 表单最大的痛点是受控组件一多、触发重渲染频繁,以及校验逻辑放得杂乱。React Hook Form 通过非受控方式减少渲染次数,Zod 提供类型安全的校验规则。
7.1 基础表单
// features/login/LoginForm.tsx import { useForm } from 'react-hook-form' import { zodResolver } from '@hookform/resolvers/zod' import { z } from 'zod' const loginSchema = z.object({ username: z.string().min(4, '用户名至少 4 位'), password: z.string().min(6, '密码至少 6 位') }) type LoginFormData = z.infer<typeof loginSchema> export default function LoginForm() { const { register, handleSubmit, formState: { errors } } = useForm<LoginFormData>({ resolver: zodResolver(loginSchema) }) const onSubmit = (data: LoginFormData) => { console.log(data) } return ( <form onSubmit={handleSubmit(onSubmit)}> <div> <input {...register('username')} placeholder="用户名" /> {errors.username && <span>{errors.username.message}</span>} </div> <div> <input type="password" {...register('password')} placeholder="密码" /> {errors.password && <span>{errors.password.message}</span>} </div> <button type="submit">登录</button> </form> ) }register 方法把 input 注册给 RHF 管理,不需要手动维护每个字段的 value 和 onChange。校验错误通过 formState.errors 取到。
7.2 与 Ant Design 集成
React Hook Form 和 Ant Design 结合时,需要把 AntD 组件的 onChange 交给 RHF 控制:
import { Controller, useForm } from 'react-hook-form' const { control, handleSubmit } = useForm<FormData>() <Controller name="role" control={control} render={({ field }) => ( <Select value={field.value} onChange={field.onChange} options={[ { value: 'admin', label: '管理员' }, { value: 'user', label: '普通用户' } ]} /> )} />Controller 的作用是把自定义组件桥接到 RHF。有了 Controller,不管是 Select、DatePicker、Radio 还是 Upload,都能用同一套状态和校验体系管理。
表单库选型时,React Hook Form 是最推荐的新方案。Formik 虽然曾是行业标准,但样板代码偏多,新项目建议直接 RHF。
8. UI 组件库与样式方案
8.1 组件库:Ant Design 仍是中后台首选
Ant Design 在中后台领域的组件覆盖度非常高,表格、表单、弹窗、消息提示、上传、日期选择都有成熟实现。使用方式:
import { Button, Table, Space, Modal } from 'antd' import { PlusOutlined } from '@ant-design/icons' <Space> <Button type="primary" icon={<PlusOutlined />}>新建</Button> <Button danger>删除</Button> </Space>AntD 在实际项目中建议配合主题定制。使用 Vite 时可以通过 ConfigProvider 统一设置主题 token:
import { ConfigProvider } from 'antd' <ConfigProvider theme={{ token: { colorPrimary: '#1677ff' } }} > <App /> </ConfigProvider>8.2 样式方案:Tailwind CSS 与 CSS Modules
Tailwind CSS 是实用性优先的原子类方案。在 Vite 项目中安装:
npm install tailwindcss @tailwindcss/vite然后配置:
// vite.config.ts import tailwindcss from '@tailwindcss/vite' export default defineConfig({ plugins: [react(), tailwindcss()] })写入入口 CSS:
@import "tailwindcss";使用方式:
<div className="flex items-center justify-between p-4 bg-white radius-lg shadow-sm"> <span className="text-sm font-medium text-gray-800">用户列表</span> <Button type="primary">新增用户</Button> </div>Tailwind 适合和组件库配合使用:AntD 管核心业务组件,Tailwind 管布局和自定义样式。不需要在 CSS 文件里给每个布局起名字。如果团队习惯了 CSS Modules 或 styled-components,也不影响,它们各自都能实现样式隔离和主题管理。
9. 动画方案:Framer Motion
页面切换、卡片入场、数字滚动、手势拖拽,这些交互用 CSS 写起来繁琐,用 Framer Motion 会更直观。
import { motion, AnimatePresence } from 'framer-motion' <motion.div initial={{ opacity: 0, y: 20 }} animate={{ opacity: 1, y: 0 }} exit={{ opacity: 0, y: -20 }} transition={{ duration: 0.3 }} > content </motion.div>路由切换动画结合 AnimatePresence 可以实现页面进出场:
<AnimatePresence mode="wait"> <motion.div key={location.pathname}> <Outlet/> </motion.div> </AnimatePresence>需要注意:动画库是有运行时开销的。轻量页面使用没有问题,但列表页大量元素同时执行动画时,会出现卡顿。生产环境建议只对首屏关键元素加动画,不要每个列表项都套 motion。
10. 跨端与框架:Next.js、React Native
10.1 Next.js:全栈 React 与服务端渲染
如果项目需要 SEO、服务端渲染或者做完整全栈应用,Next.js 是 React 生态里最主流的框架。它内置了文件路由、API 路由、服务端组件、图片优化和静态生成能力。
// app/page.tsx export default async function HomePage() { const res = await fetch('https://api.example.com/posts') const posts = await res.json() return ( <div> {posts.map((post) => ( <div key={post.id}>{post.title}</div> ))} </div> ) }App Router 下,服务端组件可以直接在组件内发起请求,不需要额外的 useEffect。这个能力对内容站特别友好。
10.2 React Native 与 Expo:移动端跨平台
React Native 是 React 语法在移动端的延伸。Expo 则进一步降低了 RN 的开发门槛,可以用手机 App 扫码预览、一键打包。
import { View, Text, StyleSheet } from 'react-native' export default function HomeScreen() { return ( <View style={styles.container}> <Text style={styles.title}>React Native Home</Text> </View> ) } const styles = StyleSheet.create({ container: { flex: 1, justifyContent: 'center', alignItems: 'center' }, title: { fontSize: 24, fontWeight: '600' } })RN 适合需要同时上架 Android 和 iOS 的团队。如果只是内部工具,也可以考虑 Web 项目直接响应式适配,不一定需要引入 RN。
11. 接口能力与批量任务的工程化组织
React 项目中的“接口能力”和“批量任务”,指的是接口层如何统一管理、页面大量并发请求如何控制、重复请求如何避免。
11.1 接口层统一管理
建议每个业务域的接口独立成文件,方便复用和维护:
// api/user.ts import request from './request' export const fetchUserList = (params: ListParams) => request.get('/api/user/list', { params }) export const createUser = (payload: CreateUserPayload) => request.post('/api/user/create', payload) export const updateUser = (id: string, payload: UpdateUserPayload) => request.put(`/api/user/${id}`, payload) export const deleteUser = (id: string) => request.delete(`/api/user/${id}`)11.2 批量请求与并发控制
React 客户端通常会遇到表格勾选批量删除、批量导出、大量明细加载等场景。这些操作如果一次发几十个请求,很容易把浏览器同时请求数打满。
推荐方案是:服务端提供批量接口,前端只发一次请求。如果服务端只有单条接口,前端可以用 p-limit 之类的库做并发控制:
import pLimit from 'p-limit' const limit = pLimit(5) const tasks = ids.map((id) => limit(async () => { await deleteUser(id) }) ) await Promise.all(tasks)TanStack Query 的 useQueries 也可以管理一组动态查询:
import { useQueries } from '@tanstack/react-query' const userQueryResults = useQueries({ queries: ids.map((id) => ({ queryKey: ['user', id], queryFn: () => fetchUserDetail(id) })) })这种方式适合页面渲染前需要同时拉取多个详情的数据场景。
12. 资源占用与性能观察
React 项目不涉及显存问题,但“资源占用”更多体现在包体积和运行时性能上。下面列出具体的观察方法。
12.1 包体积
用 Vite 构建时可以用rollup-plugin-visualizer看产物构成:
npm install -D rollup-plugin-visualizer// vite.config.ts import { visualizer } from 'rollup-plugin-visualizer' export default defineConfig({ plugins: [react(), visualizer({ open: true })] })构建后会自动打开一个体积报告页面,能看到哪些依赖占的空间大。常见处理办法:AntD 按需加载、组件库走 ESM、大模块动态 import。
12.2 运行时性能
React 页面性能观察主要用 React DevTools 的 Profiler:
- 记录一个交互过程,检查哪个组件渲染耗时高。
- 如果某个组件在输入时整体重渲,考虑是否把表单状态拆分细一点。
- 列表项用 memo 包裹,避免父组件更新时子项全部重渲。
- 长列表用 react-window 或 react-virtualized 做虚拟滚动。
TanStack Query 通过 staleTime 和 queryKey 设计,可以减少重复请求。Zustand 通过选择器避免状态未变但组件重渲的问题。React Hook Form 通过非受控组件保证表单输入不触发页面级刷新。这些都是实际项目中常用到的性能优化点。
13. React 主流库常见问题与排查
| 问题现象 | 可能原因 | 排查方式 | 解决方案 |
|---|---|---|---|
| npm install 报 ERESOLVE 依赖冲突 | 依赖版本不兼容 | 查看报错中的依赖链 | 使用 pnpm 或更新版本 |
| React Router 页面刷新后 404 | 服务器未配置 SPA fallback | 观察刷新前后 URL | 配置 rewrite 到 index.html |
| 路由切换后页面不更新 | router key 未绑定 location | 检查 AnimatePresence 或组件缓存 | 给内容区 key 绑定 pathname |
| 状态更新了但组件不渲染 | Zustand 选择器返回新对象 | 检查选择器返回值 | 使用深度等值或拆细选择器 |
| TanStack Query 数据不更新 | staleTime 太长生成了缓存 | 检查请求是否发送 | invalidateQueries 或调 short staleTime |
| React Hook Form 校验不触发 | schema 字段名和组件 name 不一致 | 对比字段名和 zod schema | 统一字段名 |
| AntD 主题不生效 | ConfigProvider 未包裹 | 检查 App 根组件 | 用 ConfigProvider 包住整个应用 |
| 动画列表卡顿 | 太多 motion 元素同时渲染 | DevTools Performance 录制 | 减少动画元素、改用 CSS 动画 |
| Next.js 服务端请求失败 | 服务端无法访问内网接口 | 查看服务端日志 | 将接口地址改为内网可访问地址 |
| React Native 启动白屏 | Metro 未启动或依赖没装 | 查看 Metro 终端日志 | 重启 Metro 并重新安装依赖 |
这些问题的共性规律是:先看终端日志,再看网络面板,最后看状态工具。React 生态的报错信息通常比较明确,React DevTools 会直接提示组件更新链路上的问题。对于接口问题,要区分是前端没发请求、请求发出但响应异常、还是响应后状态未更新这三种情况。
14. 最佳实践与使用建议
14.1 小型项目不要一上来就全家桶
一个小型工具页,只引入 React Router 和 axios 就够了。状态用 useState 或 useReducer,不需要 Zustand,更不需要 Redux Toolkit。表单不复杂时,原生表单完全满足需求。
14.2 中大型项目固定一套技术基线
中大型中后台项目建议固定:Vite + TypeScript + React Router + Zustand + TanStack Query + React Hook Form + Zod + Ant Design + Tailwind CSS。这套组合经过大量项目验证,开发效率和可维护性都平衡得比较好。
14.3 接口层和状态层分离
请求封装只放在 api 目录,不要在组件里直接写 axios。TanStack Query 的 queryKey 要规范统一,这样可以复用缓存和失效逻辑。Zustand 只放真正的全局状态,页面内部临时状态不要塞到全局 store。
14.4 设计系统先行
如果项目对 UI 一致性要求高,建议先确定使用哪个组件库和哪套样式方案。AntD 的主题 token 可以统一改色,Tailwind 的配置文件可以约束间距、字体、颜色。等页面开发到后期再换主题,改动成本会高很多。
14.5 每次升级 React 主版本时,先检查周边库兼容性
React 版本升级可能影响 React Router、Redux、相关 TypeScript 类型定义,甚至部分 Hook 行为。升级前先看每个库的 release note,改完跑一遍类型检查和全量测试。
14.6 安全和合规提醒
在 React 项目中,前端路由和页面展示可以控制,但数据安全必须由后端保证。任何涉及用户隐私或版权素材的内容,都要确认数据来源合法、展示权限正确。组件库和开源代码在商业项目中使用时,保留开源协议声明,避免协议风险。
15. 总结
React 生态的主流库并非越全越好。路由、状态管理、数据请求、表单、UI、动画、框架和跨端方案,每类库都有明确的适用场景和取舍。最推荐的第一步是先跑通一个 Vite + TypeScript 的 React 项目,然后在此基础上逐个引入 React Router、TanStack Query、Zustand、React Hook Form 和 Ant Design,用一套常见的中后台页面把链路走通。这个过程基本能覆盖大部分 React 项目的日常开发内容。
最容易踩的坑集中在三个地方:一是状态管理选型过重,项目不大却引入 Redux;二是服务端状态和客户端状态混在一起,接口数据不放 TanStack Query 而塞进全局 store;三是不看依赖版本直接升级,导致 React Router 或 AntD 类型报错。把这些点避开,React 项目的主流程会顺畅很多。
后续如果要扩展,可以从 Next.js 服务端渲染、React Native 移动端、Framer Motion 动效设计和 TanStack Query 高级缓存策略这几个方向继续深入。