gcTime vs staleTime 辨析
TanStack Query 中最容易混淆的两个时间参数:staleTime 控制数据"是否新鲜",gcTime 控制缓存"何时回收"。两者解决完全不同的问题。
#type / concept
#status / evergreen
#tech / dev / frontend
#resource / react
[!info] related notes
- 前置: TanStack Query 服务端状态
- 所属 MOC: TanStack Query 知识地图
- 关联: 缓存失效模式
- 关联: QueryClient 配置策略
gcTime vs staleTime 辨析
核心区别
staleTime → 数据"新不新"? → 决定是否需要重新请求
gcTime → 缓存"留不留"? → 决定无订阅后多久清除缓存
这两个参数完全不相关,解决的是两个独立的问题。
staleTime — 数据新鲜度
语义
数据在多久内被认为是”新鲜的”。新鲜的数据不会触发后台重新请求。
组件挂载 → useQuery 检查缓存
├─ 缓存存在且在 staleTime 内 → 直接用缓存,不请求
└─ 缓存不存在或已过 staleTime → 发请求(同时先显示缓存数据)
默认值
staleTime: 0 // 默认:数据一拿到就标记为 stale
常见配置
// 几乎实时的数据(股票、聊天)
staleTime: 0
// 用户信息,5 分钟内不重新请求
staleTime: 5 * 60 * 1000
// 配置数据,10 分钟
staleTime: 10 * 60 * 1000
// 几乎不变的数据(国家列表)
staleTime: Infinity // 永远不自动请求,只手动 invalidate
触发重新请求的条件
即使在 staleTime 内,以下操作仍会触发请求:
- 手动
invalidateQueries queryKey变化- 组件卸载后重新挂载(如果已过 staleTime)
gcTime — 垃圾回收时间
语义
当一个 query 的所有订阅者(useQuery 调用)都卸载后,缓存数据在内存中保留多久。
最后一个 useQuery 组件卸载
→ 等待 gcTime
→ 如果期间没有新的订阅者 → 删除缓存数据
→ 如果期间有新的订阅者 → 保留缓存
默认值
gcTime: 5 * 60 * 1000 // 默认 5 分钟(v5)
// v4 中叫 cacheTime,v5 改名为 gcTime
常见配置
// 默认行为:卸载 5 分钟后清除缓存
gcTime: 5 * 60 * 1000
// 保留更久:用户可能来回切换页面
gcTime: 30 * 60 * 1000
// 立即清除:卸载就删(不推荐,除非内存敏感)
gcTime: 0
// 永不清理:类似持久化缓存
gcTime: Infinity // ⚠️ 内存会持续增长
对比表
| 维度 | staleTime | gcTime |
|---|---|---|
| 控制什么 | 数据是否需要重新请求 | 缓存数据是否保留 |
| 默认值 | 0(立即 stale) | 5 分钟 |
| 影响时机 | 组件挂载 / 窗口聚焦 / 重新连接 | 最后一个订阅者卸载后 |
| 设为 0 | 每次挂载都重新请求 | 卸载后立即清除缓存 |
| 设为 Infinity | 永不自动请求(只手动 invalidate) | 缓存永不回收 |
| v4 名称 | staleTime | cacheTime |
常见错误
混淆两者
// ❌ 以为 gcTime 控制刷新频率
useQuery({ gcTime: 10_000, ... }); // 这只控制缓存保留时间
// ✅ 用 staleTime 控制刷新频率
useQuery({ staleTime: 10_000, ... });
staleTime > gcTime
// ❌ 逻辑矛盾
useQuery({
staleTime: 10 * 60 * 1000, // 10 分钟内不请求
gcTime: 1 * 60 * 1000, // 但 1 分钟后就删缓存
});
// 结果:卸载 1 分钟后缓存被删,再挂载时又要重新请求
// staleTime 的"10 分钟不重新请求"形同虚设
// ✅ gcTime 应该 >= staleTime
useQuery({
staleTime: 10 * 60 * 1000,
gcTime: 30 * 60 * 1000, // 保留 30 分钟
});
以为设了 staleTime 就不会请求
useQuery({ staleTime: 60_000 });
// 以下情况仍然会请求:
// 1. queryKey 变化
// 2. 手动 invalidateQueries
// 3. 组件首次挂载且没有缓存
// 4. 网络重连后(默认行为)
决策流程图
这个数据多久变一次?
│
├─ 几乎实时 → staleTime: 0(默认)
├─ 几分钟变一次 → staleTime: 2~5 分钟
├─ 很少变 → staleTime: 10~30 分钟
└─ 几乎不变 → staleTime: Infinity + 手动 invalidate
用户会频繁在这页和其他页之间切换吗?
│
├─ 是 → gcTime 长一些(10~30 分钟)
└─ 否 → gcTime 默认(5 分钟)即可