创见博客
服务端状态的管理(ReactQuery/SWR)
七崽爱吃小饼干2026/01/15阅读 0专栏 React

服务端状态与客户端状态的区别

服务端状态(Server State)和客户端状态(Client State)的核心区别在于存储位置、管控主体、持久化特性及使用场景,二者分工明确,共同支撑前端应用的完整交互流程。

特性服务端状态客户端状态
存储位置服务端数据库/缓存(如MySQL、Redis)客户端内存/浏览器存储(如useState、localStorage)
管控主体由后端服务维护,遵循后端业务逻辑与权限由前端应用自主管理,不受后端直接控制
持久化特性长期持久化,服务重启/客户端刷新后不丢失内存态随页面刷新丢失;持久化存储(如localStorage)仅在客户端保留
共享性多客户端/多设备共享(如用户账户信息)仅当前客户端有效,无法跨设备同步(除非主动上传至服务端)
数据来源后端接口返回,基于数据库或第三方服务前端交互生成(如表单输入值、弹窗开关状态)
核心用途存储全局共享、需持久化的业务数据存储局部交互、临时的UI/状态数据
更新方式需通过API请求(如POST/PUT)触发后端更新前端直接修改(如setState),无需网络请求
一致性保障依赖后端事务、锁机制或乐观更新策略前端自主保证,无分布式一致性问题

具体应用场景举例

  1. 服务端状态

    • 用户信息(用户名、权限、订单列表)
    • 商品数据(价格、库存、详情)
    • 系统配置(全局开关、权限规则)
    • 在React/Redux项目中,通常通过redux-thunk/redux-saga或React Query/SWR管理,处理异步请求、缓存和状态同步。
  2. 客户端状态

    • UI交互状态(弹窗显隐、Tab切换、表单输入值)
    • 本地临时数据(未提交的草稿、筛选条件缓存)
    • 设备相关配置(主题模式、语言选择)
    • 在React中,优先使用useState/useReducer存储内存态;需持久化时用localStorage/sessionStorage。

关键设计原则

  1. 状态下沉原则:优先判断状态是否需要跨组件/跨设备共享,是则归为服务端状态;否则作为客户端状态。
  2. 最小数据传输原则:服务端状态仅返回前端所需字段,减少网络开销;客户端状态避免存储冗余数据。
  3. 一致性处理:服务端状态更新后需通过接口同步到前端;客户端状态无需考虑多端一致性。

SWR & React Query

前面提到了服务端状态的管理,而 SWR 和 React Query 就是专门解决「React服务端状态管理」的两大主流库(也是当前React生态的首选方案),二者定位一致、核心能力相近,我们先讲「是什么」,再讲「解决了什么核心痛点」,最后讲区别和选型,内容会非常完整,都是实战核心知识点。


一、先明确:一个核心前提

我们之前聊过:客户端状态(UI显隐、表单输入、Tab切换)用 React 原生的 useState/useReducer 就足够了,简单高效;

而 服务端状态 是指:从后端接口/数据库请求回来的业务数据(用户信息、订单列表、商品数据、文章内容等),这类数据有几个天然特征:

  • 数据来源:远程服务器,需要发 异步请求(axios/fetch) 获取
  • 数据特性:有「过期时效」、需要「缓存」、可能「并发请求」、会「请求失败」、页面刷新会丢失需要重新请求

而这两个库,就是为「服务端状态」量身打造的 React 数据请求/状态管理库,没有之一。


二、SWR 和 React Query 是什么?

React Query (现在官方改名:TanStack Query)

  • 定位:功能最全、生态最完善、生产级的服务端状态管理库,React 生态的「事实标准」
  • 核心:基于 声明式数据请求 + 智能缓存 构建,不仅支持React,还支持Vue/Svelte/Angular(跨框架),有专门的DevTools调试工具。
  • 核心思想:把「服务端数据」当成「缓存数据」来管理,你只需要声明「要获取什么数据」,剩下的缓存、重试、刷新、加载状态、并发控制全部交给它处理。

SWR (由 Vercel 团队开发,Next.js 官方推荐)

  • 名字由来:Stale-While-Revalidate (过时数据优先展示,后台异步重新请求最新数据),这是它的核心设计理念。
  • 定位:轻量、简洁、高性能、零依赖 的服务端状态管理库,API 极简,学习成本极低,React 专属(不跨框架)。
  • 核心思想:优先展示缓存的「旧数据」保证页面不空白,同时在后台偷偷请求最新数据更新页面,极致优化用户体验,非常适合中台、后台管理系统、资讯类应用。

三、最核心:它们到底解决了什么问题?

先思考:不用它们,我们写「服务端状态请求」会有多痛苦?

日常开发中,我们用 useState + useEffect + axios/fetch 来请求接口,会遇到8个致命痛点,而且全是高频问题,每一个都要自己写大量冗余代码处理:

jsx
// 原生写法的痛点:代码繁琐、问题一堆
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);

useEffect(() => {
  const fetchData = async () => {
    try {
      setLoading(true);
      const res = await axios.get('/api/user');
      setData(res.data);
    } catch (err) {
      setError(err);
    } finally {
      setLoading(false);
    }
  };
  fetchData();
}, []);

原生写法的8个痛点:

  1. 每次请求都要手动声明 data/loading/error 三个状态,代码极度冗余,项目越大冗余越多;
  2. 数据请求成功后,没有缓存,同一个接口多次调用会发重复请求(比如多个组件用同个用户信息),浪费带宽和性能;
  3. 页面刷新/重新进入,数据会丢失,必须重新请求,用户体验差;
  4. 请求失败后,需要手动写重试逻辑,否则页面就卡死在错误状态;
  5. 数据有变更时(比如修改了用户名),需要手动写「重新请求」逻辑,否则页面数据不更新;
  6. 没有「过期数据」的概念,数据一旦获取就永远不变,无法自动刷新最新数据;
  7. 列表翻页/筛选时,并发请求会导致数据错乱(旧请求覆盖新请求);
  8. 加载状态只能全局控制,无法实现「局部加载」「骨架屏」,用户体验差。

SWR/React Query 一次性解决所有痛点,且

它们的核心价值:把「服务端状态」的所有通用问题封装成「开箱即用」的能力,开发者只需要关注「业务逻辑」,不用再写任何重复的请求处理代码。

它们解决的核心问题(全部内置,无需手动写一行代码):


核心能力1:自动管理「请求的三个核心状态」

自动为你提供 data(数据)、isLoading(加载中)、error(错误) 状态,彻底告别手动声明三个useState,代码量直接减少80%。

jsx
// SWR 极简写法
import useSWR from 'swr'
const fetcher = url => fetch(url).then(res => res.json())
const { data, isLoading, error } = useSWR('/api/user', fetcher)

// React Query 写法
import { useQuery } from '@tanstack/react-query'
const { data, isLoading, error } = useQuery({
  queryKey: ['user'], // 缓存的唯一标识
  queryFn: () => fetch('/api/user').then(res => res.json())
})

核心能力2:智能缓存机制(最核心的能力,没有之一)

  • 对请求回来的服务端数据,自动做「内存缓存+持久化缓存」,同一个接口多次调用,只会发一次请求,缓存命中直接返回数据;
  • 缓存有「唯一标识」(SWR的key,React Query的queryKey),比如 ['user', 1001] 代表用户ID=1001的信息,不同key对应不同缓存,互不干扰;
  • 缓存可以配置过期时间、失效规则,比如「用户数据5分钟过期」,过期后自动重新请求;
  • 页面刷新/组件卸载再挂载,缓存依然有效,无需重新请求,页面秒开。

核心能力3:SWR的灵魂:Stale-While-Revalidate(过期优先,后台刷新)

这是 SWR 名字的由来,也是两大库都支持的核心特性,极致优化用户体验的杀手锏:

  • 当页面需要展示数据时,优先展示缓存中「可能过期的旧数据」,保证页面永远不空白、不加载,用户能立刻看到内容;
  • 同时,在浏览器后台异步发起请求,获取最新的服务端数据;
  • 当最新数据请求成功后,自动更新页面,用户无感知;如果请求失败,页面依然展示旧数据,不会报错。

举个例子:你打开知乎的一篇文章,刷新页面时,立刻看到上一次缓存的文章内容,同时后台刷新最新的点赞数/评论数,加载完成后自动更新,这就是这个特性的效果。


核心能力4:自动重试 & 错误处理 & 乐观更新

  • 自动重试:请求失败后(比如网络波动、后端500),自动进行指数退避重试(失败后等1s重试,再失败等2s,再失败等4s),无需手动写重试逻辑;
  • 错误边界:请求失败后,返回清晰的error对象,支持手动触发「重新请求」,一键刷新数据;
  • 乐观更新:修改数据时(比如点赞、提交表单),先更新本地缓存数据,再发请求,页面立刻更新,用户体验拉满,请求成功后再和服务端同步,失败则回滚,这是中台系统的必备能力。

核心能力5:自动请求防抖、节流、并发控制

  • 对于高频请求(比如搜索框输入实时查询),自动做防抖处理,输入停止后再发请求,避免一秒发10个请求;
  • 对于列表翻页、筛选等场景,自动处理并发请求,防止旧请求的结果覆盖新请求(比如快速切换页码,不会出现第3页的数据覆盖第1页);
  • 支持「预请求」:提前请求可能需要的数据(比如鼠标悬停在列表项上,提前请求详情),页面跳转时秒开。

核心能力6:专门的「修改服务端数据」的方案(Mutation)

服务端状态不仅有「查询」(GET),还有「修改」(POST/PUT/DELETE),比如修改用户信息、提交订单、删除商品,两大库都提供了对应的解决方案:

  • SWR:useSWRMutation
  • React Query:useMutation

它们的核心能力:修改数据后,自动让相关的缓存失效,触发重新请求,保证数据一致性。 比如:你修改了用户昵称,调用修改接口后,自动让「用户信息」的缓存失效,页面立刻重新请求最新的用户信息,无需手动刷新,这是原生写法做不到的。


其他锦上添花的能力

  • 支持分页、无限滚动、数据预取,开箱即用的hooks,不用自己写滚动监听;
  • 支持依赖请求:比如先请求用户ID,再用用户ID请求用户详情,自动处理依赖关系;
  • React Query 独有:强大的DevTools调试工具,可以可视化查看缓存数据、请求状态、重试次数,开发效率拉满;
  • 零侵入:不修改你的请求逻辑(axios/fetch都能用),不修改你的组件结构,接入成本极低。

四、SWR vs React Query (核心区别 + 选型建议) 【必看】

二者90%的核心能力是一致的,都是解决服务端状态的问题,没有绝对的好坏,只有是否适合你的项目,但有几个关键区别,直接决定选型,记好这几点就够了:

核心区别(按优先级排序)

1. 体积 & 学习成本

  • SWR:极致轻量,体积只有 5KB 左右,零依赖,API 极简,只有 useSWR/useSWRMutation 几个核心hooks,学习成本极低,半小时就能上手,写起来行云流水。
  • React Query:功能全面,体积约 15KB(核心包),API 相对多一些,有queryKey/queryFn的规范,还有专门的DevTools,学习成本稍高,但文档非常完善,1-2天也能掌握核心用法。

2. 功能丰富度(生产级选型关键)

  • SWR:够用就好,极致简洁,只实现了「服务端状态」的核心功能(缓存、重试、SWR策略、mutation),没有多余的功能,适合绝大多数中小型项目,代码干净无冗余。
  • React Query:功能天花板,无所不能,除了核心能力外,还支持:
  • 分页/无限滚动的专用hooks(useInfiniteQuery);
  • 缓存的精细化控制(缓存失效、缓存预取、缓存持久化);
  • 并发请求的高级控制;
  • 专门的DevTools调试工具;
  • 支持跨框架(Vue/Svelte/Angular);
  • 支持服务端渲染(SSR)/静态生成(SSG)的完整方案。

一句话:你能想到的所有服务端状态的问题,React Query 都有解决方案。

3. 生态 & 适配性

  • SWR:由 Vercel 开发,Next.js 官方亲儿子,和 Next.js 的SSR/SSG无缝集成,React 专属,生态小而精。
  • React Query:TanStack 团队开发,跨框架、生态完善,有专门的团队维护,更新迭代快,生产环境的稳定性和兼容性更好,是大厂的首选。

4. 设计理念差异

  • SWR:极简至上,用户体验优先,核心围绕「Stale-While-Revalidate」策略,一切设计都是为了让代码简洁、页面加载更快,适合追求开发效率和用户体验的项目。
  • React Query:工程化优先,极致的可配置性,核心围绕「缓存管理」,提供了极其丰富的配置项,能满足所有复杂的业务场景,适合大型、复杂的生产级项目。

五、选型建议

选 SWR 的场景(推荐)

  1. 你的项目是 React/Next.js 开发的,中小型项目(中台、后台管理系统、资讯类应用);
  2. 你追求 极简的API、极低的学习成本、干净的代码,不想写太多配置;
  3. 你最看重 用户体验(页面秒开、不加载、优先展示旧数据);
  4. 项目的业务逻辑不复杂,不需要太多精细化的缓存控制。

选 React Query (TanStack Query) 的场景(推荐)

  1. 你的项目是 大型生产级项目(电商、社交、金融类应用),业务逻辑复杂,需要精细化的缓存控制;
  2. 你需要 分页、无限滚动、预请求、乐观更新 等高级功能;
  3. 你需要 调试工具 来提高开发效率,排查问题;
  4. 你的团队可能需要跨框架开发(比如同时写React和Vue),想统一技术栈;
  5. 你需要 极致的稳定性和可维护性,大厂项目首选。

六、总结

  1. SWR/React Query 不是替代 Redux/Mobx,而是专门解决服务端状态的问题,客户端状态(UI显隐、表单输入)依然用 useState/useReducer 就够了;
  2. 核心价值:把「服务端状态」的请求、缓存、重试、刷新、状态管理全部封装,让开发者只关注业务逻辑,彻底告别手写 useState+useEffect 的冗余代码;
  3. 两大核心特性:智能缓存 + Stale-While-Revalidate,前者解决性能问题,后者解决用户体验问题;
  4. 选型口诀:小项目/追求简洁 → SWR,大项目/追求功能全面 → React Query。
评论
0/100