在前端项目中,数据请求与缓存一直是令人头疼的事。TanStack Query(原名 React Query)把「服务端状态」从组件里剥离出来,用统一的缓存、失效与重试机制,让前端数据请求变得可预测、可维护。
一、为什么还需要 TanStack Query
用 useState + useEffect 手写数据请求时,我们往往要重复处理 loading、error、竞态、缓存失效,还要在多个组件间手动同步同一份数据。这些「服务端状态」与「客户端状态」(如表单输入、开关)有本质区别:服务端状态属于后端、会被多人修改、需要失效与重新拉取。
TanStack Query 的核心价值,就是接管这些服务端状态:它用 queryKey 作为缓存索引,自动去重并发请求、管理新鲜度、处理失败重试,并天然支持乐观更新。配合一份规范的后端 API(例如用 FastAPI 落地 Python 异步接口),前后端协作会顺畅很多。
二、快速上手
2.1 安装
npm install @tanstack/react-query
# 或
pnpm add @tanstack/react-query
2.2 挂载 QueryClientProvider
import { QueryClient, QueryClientProvider, useQuery } from '@tanstack/react-query'
const queryClient = new QueryClient()
export default function App() {
return (
<QueryClientProvider client={queryClient}>
<UserList />
</QueryClientProvider>
)
}
2.3 发起第一个查询
function UserList() {
const { data, isLoading, error } = useQuery({
queryKey: ['users'],
queryFn: () => fetch('/api/users').then((r) => r.json()),
})
if (isLoading) return <p>加载中…</p>
if (error) return <p>请求失败</p>
return (
<ul>{data.map((u) => <li key={u.id}>{u.name}</li>)}</ul>
)
}
注意:queryKey 是缓存的唯一索引,相同 key 会自动复用缓存与去重请求,这也是它比「每次 useEffect 里写 fetch」强的地方。
三、理解 queryKey 与缓存生命周期
缓存行为由 staleTime(新鲜期,期间不重新请求)和 gcTime(回收期,无引用后多久清理)控制。在 QueryClient 里统一配置默认值,比在每个 hook 里重复写更省心:
const queryClient = new QueryClient({
defaultOptions: {
queries: {
staleTime: 30_000, // 30s 内视为新鲜,不重新请求
gcTime: 5 * 60_000, // 5min 无引用才回收缓存
retry: 2, // 失败重试 2 次
refetchOnWindowFocus: false, // 窗口聚焦不强制刷新
},
},
})
带参数的查询要把参数写进 queryKey,例如 queryKey: ['user', id],这样不同 id 各自缓存、互不干扰。
四、数据变更与乐观更新
写操作交给 useMutation。真正的体验提升来自「乐观更新」:在请求发出前就先更新缓存,失败再回滚,成功后再失效重新拉取。
const mutation = useMutation({
mutationFn: (newUser) =>
fetch('/api/users', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(newUser),
}).then((r) => r.json()),
onMutate: async (newUser) => {
await queryClient.cancelQueries({ queryKey: ['users'] })
const prev = queryClient.getQueryData(['users'])
queryClient.setQueryData(['users'], (old) => [...old, newUser])
return { prev }
},
onError: (_e, _u, ctx) => {
if (ctx?.prev) queryClient.setQueryData(['users'], ctx.prev)
},
onSettled: () => {
queryClient.invalidateQueries({ queryKey: ['users'] })
},
})
这套「取消旧请求 → 暂存快照 → 乐观写入 → 失败回滚 → 成功后失效」的模式,是 TanStack Query 在生产环境最常用的写法。
五、Vue 生态同样好用
如果你用的是 Vue 3 而非 React,直接安装 @tanstack/vue-query,API 与 React 版几乎一致,只是组件换成 useQuery 的组合式写法。同一套缓存心智模型可以同时覆盖两个技术栈,迁移成本极低。
六、生产环境最佳实践
| 检查项 | 推荐配置 | 说明 |
|---|---|---|
| 缓存新鲜度 staleTime | 30s–5min | 避免频繁打扰后端,热点数据可更长 |
| 失败重试 retry | 网络错误重试 1–2 次 | 5xx 谨慎重试,避免雪崩 |
| 并行请求 | useQueries 批量拉取 | 减少请求瀑布流 |
| 占位数据 placeholderData | keepPreviousData | 分页切换更顺滑 |
| 依赖请求 enabled | 条件触发 | 避免无谓请求 |
| 缓存回收 gcTime | ≥5min | 平衡内存占用与命中率 |
七、三个常见踩坑
1. queryKey 不稳定。把对象或函数直接塞进 key 会导致每次渲染都生成新 key,缓存永远命中不了。一律用可序列化、稳定的数组(如 ['user', id])。
2. 又在 useEffect 里手写 fetch。这会重新引入 loading/error 样板与竞态问题,等于放弃了缓存层。新项目应默认走 useQuery。
3. 乐观更新忘记回滚。只写 onMutate 不写 onError 回滚,一旦接口报错界面就会停留在错误数据上。务必成对出现。
八、小结
TanStack Query 不是「又一个请求库」,而是服务端状态的管理层。它用缓存索引、新鲜度策略和乐观更新,把前端数据请求从重复样板中解放出来。配合容器化的部署(如 Docker 编排)与 GitHub Actions 的自动化测试,就能交付稳定可维护的前端数据层;日常开发中用 VS Code 配合类型提示,体验会更顺滑。




