数据请求库
本教程共 50 篇 · 第 36 篇 · 更新于 2026-07-29 · 约 7 分钟阅读
36. 数据请求库
本节目标:理解为什么需要数据请求库,上手 SWR 和 TanStack Query 的基本用法,知道它们解决什么问题。
上一章那个 Hook 缺了什么
我们在上一章手写了 useFetch。它能跑,但离生产级还差不少:
- 没有缓存:组件销毁再挂载,重新发请求
- 没有去重:同一页面多个组件请求同一个 URL,各自发各的
- 没有自动重验证:窗口重新获得焦点时不会刷新数据
- 没有分页/无限加载支持
- 没有乐观更新
自己写这些能写到怀疑人生。社区早就有成熟方案了。
SWR:先试试水
SWR(stale-while-revalidate)是 Vercel 出的一套轻量数据请求 Hook。名字来自一种缓存策略:先用缓存数据,后台重新验证,拿到新数据再更新。
npm install swr
基本用法:
import useSWR from 'swr';
const fetcher = url => fetch(url).then(res => res.json());
function UserProfile({ userId }) {
const { data, error, isLoading } = useSWR(
`/api/users/${userId}`,
fetcher
);
if (isLoading) return <p>加载中……</p>;
if (error) return <p>出错了</p>;
return <h1>{data.name}</h1>;
}
就两行配置,自动帮你搞定:缓存、去重、聚焦重验证、轮询。
Note
useSWR的第一个参数是 key。key 变化就会重新请求。多个组件用同一个 key,只会发一次请求。
SWR 的配置
可以全局配置 fetcher 和默认行为:
import { SWRConfig } from 'swr';
function App() {
return (
<SWRConfig value={{
fetcher: url => fetch(url).then(res => res.json()),
revalidateOnFocus: true, // 窗口聚焦时重验证
dedupingInterval: 5000, // 5 秒内重复请求去重
errorRetryCount: 3, // 失败后重试 3 次
}}>
<UserProfile userId={1} />
</SWRConfig>
);
}
SWR 的 mutation
需要手动触发重新验证时,用 mutate:
import { useSWRConfig } from 'swr';
function RefreshButton() {
const { mutate } = useSWRConfig();
return (
<button onClick={() => mutate('/api/users/1')}>
刷新
</button>
);
}
TanStack Query:重武器
TanStack Query(原名 React Query)功能更全,适合复杂场景。
npm install @tanstack/react-query
基本用法:
import { QueryClient, QueryClientProvider, useQuery } from '@tanstack/react-query';
const queryClient = new QueryClient();
function App() {
return (
<QueryClientProvider client={queryClient}>
<UserProfile userId={1} />
</QueryClientProvider>
);
}
function UserProfile({ userId }) {
const { data, isLoading, error } = useQuery({
queryKey: ['user', userId],
queryFn: async () => {
const res = await fetch(`/api/users/${userId}`);
if (!res.ok) throw new Error('请求失败');
return res.json();
},
});
if (isLoading) return <p>加载中……</p>;
if (error) return <p>出错了:{error.message}</p>;
return <h1>{data.name}</h1>;
}
两个关键概念:queryKey 和 queryFn。queryKey 是缓存的标识,变化就重新请求。
TipqueryKey 用数组形式
['user', userId],可以精确控制缓存粒度。所有用相同 queryKey 的请求共享缓存。
TanStack Query 的 mutation
更新数据用 useMutation:
import { useMutation, useQueryClient } from '@tanstack/react-query';
function UpdateNameForm({ userId }) {
const queryClient = useQueryClient();
const mutation = useMutation({
mutationFn: async newName => {
const res = await fetch(`/api/users/${userId}`, {
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name: newName }),
});
return res.json();
},
onSuccess: () => {
// 成功后让相关缓存失效,自动重新请求
queryClient.invalidateQueries({ queryKey: ['user', userId] });
},
});
return (
<button
disabled={mutation.isPending}
onClick={() => mutation.mutate('新名字')}
>
{mutation.isPending ? '保存中……' : '保存'}
</button>
);
}
两个库的对比
| 特性 | SWR | TanStack Query |
|---|---|---|
| 体积 | ~4KB | ~13KB |
| 学习曲线 | 低 | 中 |
| 缓存策略 | 内置 | 高度可配 |
| 分页/无限加载 | 有 (useSWRInfinite) | 有 (useInfiniteQuery) |
| DevTools | 简单 | 功能全 |
| 乐观更新 | 手动 | 内置支持 |
| 离线支持 | 较弱 | 较强 |
我的建议:简单项目用 SWR,够用且轻量。复杂项目、需要精细控制缓存、频繁做 mutation 的,上 TanStack Query。
Warning这两个库都基于 Suspense 边界做了 React 19 适配。如果你已经在用 React 19 的
useHook,很多场景下可能不需要额外的请求库了。
数据请求的通用套路
不管用哪个库,数据请求都遵循这几个套路:
- 请求成功 → 展示数据
- 请求中 → 显示加载态(spinner 或骨架屏)
- 请求失败 → 显示错误 + 重试按钮
- 数据过期 → 后台重新验证,不打断用户
手动写这些逻辑不难,但到处重复就烦了。请求库帮你把样板代码吃掉,你专注业务就行。
React 19 的 use Hook 来了
React 19 引入了 use Hook,可以在组件里直接读取 Promise:
import { use, Suspense } from 'react';
function Albums({ albumsPromise }) {
const albums = use(albumsPromise);
return (
<ul>
{albums.map(album => <li key={album.id}>{album.title}</li>)}
</ul>
);
}
function App() {
return (
<Suspense fallback={<p>加载中……</p>}>
<Albums albumsPromise={fetchData('/albums')} />
</Suspense>
);
}
这是 React 官方的数据获取方案,后面章节会详细讲。但 use 本身不提供缓存、重验证这些能力,所以和 SWR/TanStack Query 不是替代关系,是互补的。