12k
All articles

现代 React 应用背后的库生态

2026年现代 React 技术栈指南:Next.js、TanStack Query、Zustand、Tailwind v4、shadcn/ui、React Hook Form、Motion 及 RSC 兼容选择。

OpenReplay Team
OpenReplay Team
现代 React 应用背后的库生态

现代 React 应用的核心构成是 React 本身,加上一套小而可预期的库:一个用于服务端状态数据获取,一个用于客户端状态,一个用于路由,一个用于样式,一个用于 UI 基础组件,一个用于表单。React 本身——目前是 19.2 版本线——负责组件、hooks 和渲染。其余的都是选择,而生态系统的快速迭代让这些选择看起来比实际上更难。现实情况是,对于每项工作,如今都有一个明确的默认选项和一两个适用于特定场景的替代方案。

本文按”待完成的工作”来梳理这套技术栈:基础框架、状态管理、数据获取、路由、样式、UI 组件、表单和动画。对于每个类别,你将了解 2026 年的默认选择、何时应选用替代方案,以及该选择如何与重塑生态系统的两股力量——React Server Components(RSC)和 React Compiler——相互作用。

核心要点

  • 现代 React 应用将状态分为四个桶——服务端状态(TanStack Query)、客户端 UI 状态(useState 或 Zustand)、URL 状态(nuqs)和全局应用状态(Zustand)——为错误的桶选择错误的工具,是产生不必要复杂性的最常见根源。
  • TanStack Query 的 staleTime 默认为 0,因此每次组件挂载都会触发后台重新获取;对于稳定数据,将其设置为非零值是大多数应用从未进行的、杠杆效应最高的配置项。
  • React Compiler 于 2025 年 10 月 7 日发布 1.0 版本,自动对组件进行记忆化处理,消除了库集成代码中大多数手动的 useMemo/useCallback 调用。
  • 运行时 CSS-in-JS 与 RSC 存在冲突;在使用 RSC 的 Next.js 应用中,Tailwind CSS v4 或 CSS Modules 是兼容的默认样式方案。
  • shadcn/ui 默认使用 Radix 作为基础组件,并于 2026 年初将 Base UI 添加为官方替代方案;你完全拥有组件代码的所有权,而不是依赖一个版本化的包。

React 项目基础:选择你的框架

在做任何库的选择之前,你需要先选定应用构建的基础——在 2026 年,这个决定在很大程度上取决于你是否需要 React Server Components。这里要完成的工作是:项目脚手架、构建工具、路由和渲染策略,全部打包在一个起点中。

对于大多数新应用,Next.js 是默认选择:它是最完整的全栈 React 框架,具备稳定的 RSC、Server Actions 和基于文件系统的路由。当你构建客户端渲染的单页应用,并希望拥有快速、无主观约束的构建工具而无需完整框架时,选择 Vite——你需要自行组装路由和数据获取。当项目以内容为主——营销页面、文档、博客——并且你希望输出以静态 HTML 为主,仅在需要交互的地方使用 React islands 时,选择 Astro。当类型安全的路由和服务端函数是首要考量,且你能接受使用早期版本时,选择 TanStack Start:它目前处于 v1 Release Candidate 阶段,即将稳定,RSC 支持仍在开发中。

在所有这些选择中,有两股力量值得权衡:React Server Components 将数据获取转移到服务端,以及 React Compiler 无论你选择哪种基础框架,都会自动对组件进行记忆化处理。

React Compiler 对库选择的影响

React Compiler 现已稳定,并改变了你需要手动编写的优化代码。React Compiler 1.0 于 2025 年 10 月 7 日在 React Conf 上发布,它在构建时自动对组件和 hook 值进行记忆化处理,消除了大多数手动的 useMemouseCallback 调用。这对库的选择意义重大,因为曾经充斥在集成代码中的一类样板代码——包装 selector、对传递给表单字段的回调进行记忆化、稳定派生值——现在都由编译器处理了。

对库选择的实际影响:自动记忆化削弱了原子状态库在性能方面的论据,并消除了表单集成中的一类样板代码,因此你选择库时应基于其数据模型和开发体验,而非基于需要多少手动调优才能避免重渲染风暴。根据 React Compiler 安装文档,该编译器支持 Vite、Next.js 和 Expo。

React 状态管理库:四桶分类法

现代 React 应用有四个不同的状态桶——由 TanStack Query 管理的服务端状态、由 useStateZustand 处理的客户端 UI 状态、由 nuqs 管理的 URL 状态,以及存放在 Zustand 中的全局应用状态——为错误的桶选择错误的工具,是产生不必要复杂性的最常见根源。大多数”状态管理很难”的痛苦,来自于将服务端数据放入客户端 store,或者将本应存放在 URL 中的共享 UI 状态建模为全局状态。

状态桶存放内容2026 年默认选择何时选用替代方案
服务端状态远程数据、缓存、数据变更TanStack Query大规模使用 GraphQL → Apollo Client;TypeScript 全栈 → tRPC
客户端 UI 状态本地组件状态、开关、草稿useState/useReducer状态需在远距离组件间共享 → Zustand
URL 状态过滤器、选项卡、分页nuqs
全局应用状态跨应用的会话、主题、购物车Zustand已有 Redux 代码库 → Redux Toolkit

有了 TanStack Query 处理服务端状态、内置 hooks 处理本地状态,以及 URL 处理共享 UI 状态,需要专用全局 store 的场景已大幅缩减。当你确实需要时,Zustand 是默认选择——一个极简的 create() store,支持 selector 订阅,无需 Provider:

import { create } from 'zustand'

const useCartStore = create((set) => ({
  items: [],
  addItem: (item) => set((state) => ({ items: [...state.items, item] })),
}))

当你在维护或扩展现有 Redux 代码库,并希望使用其 DevTools 时间旅行和结构化 action 追踪时,选择 Redux Toolkit。当你确实需要细粒度原子订阅时,选择 Jotai——尽管随着 React Compiler 自动记忆化的加入,原子模型在减少重渲染方面的论据已不如以前有力。对于 URL 状态,nuqs 为你提供类型化的搜索参数,使过滤器和分页状态在刷新后得以保留,并可通过链接方便地共享。

服务端状态与 staleTime 默认值

TanStack Query 的 staleTime 默认为 0,这意味着每次组件挂载都会将数据标记为过期并触发后台重新获取;对于导航菜单或用户资料等稳定数据,将 staleTime 设置为非零值,是大多数应用从未进行的、单一杠杆效应最高的配置变更。官方重要默认值文档对此有明确说明:

import { useQuery } from '@tanstack/react-query'

// staleTime 默认为 0 → 每次挂载都会重新获取。
// 对于不随视图变化的数据,设置非零值。
const { data } = useQuery({
  queryKey: ['profile'],
  queryFn: fetchProfile,
  staleTime: 5 * 60 * 1000, // 5 分钟
})

一种常见的生产环境故障模式是保留此默认值:对数据密集型应用的会话回放,经常会发现每次路由变化都触发大量后台重新获取的瀑布流——这种模式很难归因于某一行配置,直到你将网络请求序列与真实用户会话的回放进行对比才会发现。

React 数据获取与路由库

对于数据获取,默认选择取决于你在哪里获取数据。对于客户端获取——REST 或 GraphQL 缓存、乐观更新、后台同步——TanStack Query 是默认选择;在 Next.js 16 等 RSC 框架中,你在 Server Components 内部于服务端获取数据并作为 props 向下传递,完全避免使用客户端数据获取库。当你以 GraphQL 为核心并需要规范化缓存时,选择 Apollo Client;当你在 TypeScript 中同时控制客户端和服务端,并希望无需 schema 层即可实现端到端类型推断时,选择 tRPC

路由遵循同样的服务端/客户端分离原则。如果你使用元框架,它会处理路由。在元框架之外,React Router 是使用最广泛的库;它同时提供经典的客户端库模式和带有 loaders、actions 及服务端渲染的完整框架模式。当类型安全的路由推断是优先考量时,选择 TanStack Router——它在 v1 版本已稳定,为参数和搜索提供业界一流的 TypeScript 推断。

TanStack Start——提供类型安全的基于文件的路由、服务端函数和 SSR——正以 v1 Release Candidate 的形式接近稳定;根据 TanStack Start 文档,RSC 支持仍在开发中,这使得 Next.js 16 对于今天需要 RSC 的团队来说是更完整的全栈选择。将其视为值得关注的新兴方案,而非当前的默认选择。

React 样式库与 RSC 兼容性

RSC 兼容性现在是样式选择的首要过滤条件。styled-components 和 Emotion 等运行时 CSS-in-JS 库在浏览器中执行样式注入,这与 React Server Components 在服务端渲染的模型相冲突——因此在使用 RSC 的 Next.js 16 应用中,Tailwind CSS v4 或 CSS Modules 是兼容的默认选择,而非运行时 CSS-in-JS。

Tailwind CSS 是实用优先的默认选择。根据 Tailwind v4 公告,v4 版本通过 @theme 指令将配置移入 CSS,并搭载了更快的引擎。单个 className 即可承载所有样式:

<h1 className="text-blue-700">{title}</h1>

当你希望使用与组件共置的、作用域隔离的 CSS,且不想学习实用类词汇表时,选择 CSS Modules——它不会产生任何泄漏,并且在 RSC 中运行良好。当你希望以 TypeScript 编写类型安全的样式,同时没有运行时开销时,选择构建时 CSS-in-JS 方案,如 vanilla-extract

styled-components 自 2025 年 3 月起进入维护模式,不推荐用于新项目,但它并非废弃软件——它仍然可以在 'use client' 边界后的 RSC 中使用,并在其 v6.4 版本中通过 createTheme 提供了兼容 RSC 的 CSS 变量主题化方案。

更广泛的结论依然成立:运行时样式注入与服务端渲染模型相冲突,因此实用优先或构建时方案是新 RSC 应用更安全的选择。

React UI 库:依赖模型 vs. 所有权模型

UI 层最重要的决策是架构层面的:安装一个版本化的组件库,还是完全拥有组件代码。样式化组件库——MUIMantineChakra UIAnt Design——以依赖包的形式为你提供一套精美的、可主题化的设计系统,你可以随时间推移进行升级。由 shadcn/ui 推广的所有权模型则是将组件源代码复制到你的项目中。

shadcn/ui 的复制粘贴模型意味着你完全拥有组件代码——没有需要升级的版本化依赖,没有破坏性变更迁移,也没有无法覆盖的库作者设计决策——这在架构上与安装 MUI 或 Mantine 有本质区别,也解释了为何它已成为在 Tailwind 上构建自定义设计系统的团队的默认选择。它的 GitHub star 数已超过 11 万

// 所有权模型:Button 存在于你的代码库中,你可以直接编辑它。
import { Button } from '@/components/ui/button'

// 依赖模型:Button 来自一个版本化的包。
import { Button } from '@mui/material'

shadcn/ui 基于无头原语构建其组件,默认原语是 Radix。根据 shadcn/ui 文档,Base UI 于 2026 年初被添加为官方替代原语,但 Radix 仍是推荐的默认选项。

Radix 自 WorkOS 于 2022 年收购 Modulz 后一直由其维护,部分开发者认为其发布节奏有所放缓——这也是 Base UI(来自 MUI 团队)作为替代方案出现的背景——尽管 shadcn/ui 仍默认使用 Radix,后者依然在 MIT 协议下积极维护并保持开源。当你希望使用来自 MUI/Floating UI 体系的原语,并需要样式引擎灵活性时,选择 Base UI;当你在构建数据密集型企业级界面,并希望开箱即用地获得表格、表单和日期选择器,而不是自行组装时,选择 Ant Design 这样的完整样式库。

React 表单库

React Hook Form 是现代 React 中表单处理的默认选择。其设计采用非受控模式:字段通过 ref 注册,而非在 React state 中保存每次击键,这从根本上最小化了重渲染。通过 schema 解析器处理验证,典型的搭配是 React Hook Form 加上 Zod 进行运行时验证和类型推断。

import { useForm } from 'react-hook-form'

const { register, handleSubmit } = useForm()

根据 React Hook Form 文档register 将字段连接到表单,而不使其成为受控组件。随着 React Compiler 自动记忆化的加入,集成代码中曾经需要围绕字段处理器进行的手动回调记忆化已被消除,进一步简化了连接过程。当你需要对复杂的深层嵌套表单状态进行完全类型安全的处理时,选择 TanStack Form;当你围绕 Server Actions 构建渐进增强的表单时,选择 Conform。Formik 和 React Final Form 实际上已停止维护——新项目请避免使用它们。

表单中一种常见的生产环境故障模式是受控输入在每次击键时更新状态导致的过度重渲染。非受控模型产生更少的中间状态事件,这在会话回放中表现为更清晰的交互轨迹——从用户开始输入到表单提交之间,冗余渲染更少。

React 动画库

Motion 是 React 事实上的默认动画库。Motion——即前身为 Framer Motion 的库——使用包名 motion 和导入路径 motion/react,其 v12 主版本已完全支持 React 19,每月 npm 下载量约达 3000 万次

声明式 API 通过 initialanimate props 满足大多数应用的动画需求:

import { motion } from 'motion/react'

<motion.div initial={{ opacity: 0 }} animate={{ opacity: 1 }} />

根据 Motion 文档,它涵盖手势、布局过渡以及用于退出动画的 AnimatePresence。一个无障碍访问注意事项:请遵守 prefers-reduced-motion 媒体查询,MDN 将其记录为用户希望减少动效的标准信号——Motion 允许你读取该值并据此控制动画。当你希望以物理弹簧动画作为核心模型时,选择 react-spring;当你需要对多个元素进行精细的时间轴编排时,选择 GSAP

配套工具:身份验证、测试、图表与国际化

除核心技术栈外,还有几个类别是大多数应用的必备——每类各有一个合理的默认选择:

每个类别本身都是一个深度话题;将这些视为起点,然后参阅各自的文档深入了解。

2026 年 React 技术栈一览

对于今天的 Next.js 16 应用,兼容的默认选择按工作职责排列如下:

类别2026 年默认选择何时选用替代方案RSC 兼容性
服务端状态TanStack Query大规模 GraphQL → Apollo;全栈 TS → tRPC是(客户端 islands)
客户端/全局状态Zustand已有 Redux → Redux Toolkit是('use client'
URL 状态nuqs
路由Next.js / React Router类型安全推断 → TanStack Router
样式Tailwind v4作用域 CSS → CSS Modules
UI 基础组件shadcn/ui + RadixMUI 体系 → Base UI
动画Motion(motion/react物理动画 → react-spring;时间轴 → GSAP'use client'
表单React Hook Form + Zod复杂嵌套状态 → TanStack Form'use client'

结语

生态系统的快速迭代确实存在,但决策空间其实很小:为你的状态选择正确的桶,在框架允许的情况下在服务端获取数据,UI 层默认使用 Tailwind 和 shadcn/ui,动画和表单分别使用 Motion 和 React Hook Form。现在值得深入理解的两个变化是:React Compiler 消除了集成代码中大多数手动记忆化操作,以及 RSC 兼容性是一个硬性过滤条件——运行时 CSS-in-JS 和尚不稳定的 RSC 支持,缩小了适用于 Next.js 16 应用的选择范围。从这些默认选项出发,只有当特定需求——GraphQL 规模、类型安全路由、物理动画——真正有此要求时,再换用替代方案。

常见问题

styled-components 可以与 React Server Components 一起使用吗?

styled-components 只能在 'use client' 边界后与 React Server Components 配合使用,因为它在浏览器中运行时注入样式,这与 RSC 的服务端渲染模型相冲突。该库于 2025 年 3 月进入维护模式,不推荐用于新项目,但它仍在发布更新,并在 v6.4 版本中添加了兼容 RSC 的 CSS 变量主题化方案。对于新的 RSC 应用,Tailwind CSS v4 或 CSS Modules 是兼容的默认选择,因为两者都在构建时而非运行时生成样式。

shadcn/ui 的所有权模型与安装 MUI 等库有何区别?

shadcn/ui 将组件源代码直接复制到你的项目中,因此你完全拥有并可以直接编辑这些文件,无需升级版本化依赖,也无需处理破坏性变更迁移。MUI 和 Mantine 作为版本化包安装,你通过导入并随时间升级来使用,以可管理的更新换取定制控制权的让渡。所有权模型适合希望完全掌控覆盖能力、构建自定义设计系统的团队;依赖模型适合希望使用精美、有人维护的设计系统而无需拥有代码的团队。

何时应使用 Zustand 而非 useState 来管理客户端状态?

当状态需要在没有直接父子关系的远距离组件之间共享时,使用 Zustand,例如从多处访问的购物车、主题或会话数据。对于单个组件或小型子树的本地状态,如表单草稿和开关,使用 useState 或 useReducer。对纯本地状态使用 Zustand 会增加不必要的间接层,而将真正的全局状态保存在 prop drilling 或 context 中则会造成不必要的重渲染和耦合问题。

React Compiler 是否可以替代 TanStack Query 或状态管理库?

不能。自 2025 年 10 月 7 日起稳定发布的 React Compiler 1.0 在构建时自动对组件和 hook 值进行记忆化处理,消除了大多数手动的 useMemo 和 useCallback 调用,但它不管理服务端状态缓存、后台重新获取或跨组件状态共享。你仍然需要 TanStack Query 处理服务端状态,以及 Zustand 或 useState 处理客户端状态。编译器改变的是这些集成所需的手动调优程度,而非是否需要这些库本身。

DevTools for the frontend

Gain Debugging Superpowers

Unleash the power of session replay to reproduce bugs, track slowdowns and uncover frustrations in your app. Get complete visibility into your frontend with OpenReplay — the most advanced open-source session replay tool for developers.

Star on GitHub12k

We use cookies to improve your experience. By using our site, you accept cookies.