news 2026/9/10 4:35:08

TanStack Preact Query 的重要默认配置(Important Defaults)深度解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
TanStack Preact Query 的重要默认配置(Important Defaults)深度解析

TanStack Preact Query 的重要默认配置(Important Defaults)深度解析

【免费下载链接】query🤖 Powerful asynchronous state management, server-state utilities and data fetching for the web. TS/JS, React Query, Solid Query, Svelte Query and Vue Query.项目地址: https://gitcode.com/GitHub_Trending/qu/query

TanStack Preact Query 开箱即用,但默认值往往是新用户"踩坑"与调试困难的源头:缓存数据一律视为过期、失败静默重试 3 次、闲置查询 5 分钟后被回收……本指南以 docs/framework/preact/guides/important-defaults.md 为骨架,逐条拆解 Preact Query 各项默认行为,并结合仓库内@tanstack/query-core源码给出实现依据与自定义方案。读完你将能熟练通过staleTimegcTimeretry等选项掌控数据的过期、回收与重试节奏,让请求行为完全符合预期。

默认值设计哲学:aggressive but sane

TanStack Query 系列(Preact Query、React Query、Vue Query、Solid Query 等)共享同一套查询内核@tanstack/query-core。在 Preact 应用中,packages/preact-query/src/index.ts 直接export * from '@tanstack/query-core',因此本文讨论的默认值行为在所有框架适配层上完全一致。

这些默认值被官方定位为aggressive but sane(激进但理智):它们追求"开箱即用即可获得合理体验",但若使用者对其不了解,往往会因出乎意料的自动行为而困惑。下文是每一个默认行为的完整盘点,以及对应的调节入口。

默认一:缓存数据一经获取即视为 stale

通过useQueryuseInfiniteQuery创建的查询实例,默认会把缓存中已有的数据视为过期(stale)

这背后对应staleTime的默认值为0。其语义是:只要新查询实例挂载、窗口重新聚焦或网络恢复,库就会去后台重新请求以刷新数据。想要改变该行为,可以全局按查询设置staleTime

staleTime 的三种典型取值

staleTime表示"数据在多少毫秒内保持新鲜"。设置了staleTime的查询,在该时间到期前会被视为fresh(新鲜)

  • 设置staleTime: 2 * 60 * 1000:2 分钟内数据始终从缓存直接读取,不会触发任何形式的 refetch,除非查询被手动失效;
  • 设置staleTime: Infinity:永不因过期而触发 refetch,直到查询被手动失效;
  • 设置staleTime: 'static'永不触发 refetch,即使查询被手动失效也无效。

'static' 与 Infinity 的关键差异

'static'Infinity都能阻止基于过期的自动 refetch,但'static'更严格:

  • queryClient.invalidateQueries()可以失效staleTime: Infinity的查询,但对staleTime: 'static'的查询没有任何作用
  • 设置为"always"refetchOnMountrefetchOnWindowFocusrefetchOnReconnect也一律被'static'阻止。

该差异在源码中体现得非常直接。packages/query-core/src/query.ts 中isStaleByTime()的判定顺序是:无数据视为 stale →'static'直接返回false(永不 stale)→ 已被 invalidated 视为 stale → 依据dataUpdatedAtstaleTime计算剩余新鲜时间。正因为'static'的短路判断位于 invalidated 判断之前,失效操作对它无效。此外 query.ts 的isStatic()会检查当前观察者中是否存在staleTime: 'static'的选项。

选型建议

  • 'static'适合应用运行期间绝不可能变化的数据——启动时拉取的 feature flags、登录后加载的用户权限、静态参考表等;
  • Infinity适合仍然希望保留手动失效能力的数据。

默认二:stale 查询在三个时机自动后台刷新

stale 查询会在以下时机被自动后台 refetch:

  1. 新的查询实例挂载refetchOnMount生效);
  2. 窗口重新聚焦refetchOnWindowFocus生效);
  3. 网络重新连接refetchOnReconnect生效)。

设置更长的staleTime是避免过度 refetch 的推荐做法;此外也可以直接定制这三个触发点,例如将refetchOnWindowFocus设为false'always',精细控制自动刷新的发生时机。其中refetchOnReconnect还有一个底层联动:在 packages/query-core/src/queryClient.ts 中,当它未显式定义时会被默认推导为networkMode !== 'always',即"网络模式下才在重连时刷新"。

三个触发点在query层同样有对应方法(如 query.ts 的onFocus()onOnline()),分别由 focus 与在线状态管理器驱动。

默认三:refetchInterval 轮询与 staleTime 相互独立

查询可选用refetchInterval选项周期性地触发 refetch,它与staleTime相互独立的两套机制——即使数据仍新鲜,只要设置了refetchInterval就会按固定间隔轮询。具体用法详见 Polling(轮询)指南。

默认四:闲置查询保留 5 分钟后回收

当某个查询不再有任何活跃实例(useQueryuseInfiniteQuery或底层观察者都被卸载)时,它会被标记为inactive(闲置),但仍然留在缓存中,以备后续再次使用。默认情况下,闲置查询会在5 分钟后被垃圾回收(garbage collected)。

对应源码是 packages/query-core/src/removable.ts 的updateGcTime()

// Default to 5 minutes (Infinity for server-side) if no gcTime is set this.gcTime = Math.max( this.gcTime || 0, newGcTime ?? (isServerEnvironment() ? Infinity : 5 * 60 * 1000), )

值得注意的两点实现细节:

  • 默认回收时长为1000 * 60 * 5毫秒,即 5 分钟;服务端环境(SSR)默认为Infinity,避免回收尚未水合所需的数据;
  • 回收时长取gcTime最大值,且调度逻辑(removable.ts)会先清除旧定时器再重新调度,因此调大gcTime后旧数据不会立刻被误回收。

如要改变该时长,可将全局或单查询的gcTime改为其他毫秒值。例如对"重新进入页面频率较高、希望更久地保留状态"的查询设置gcTime: 30 * 60 * 1000

默认五:失败查询静默重试 3 次,指数退避

查询失败时,默认静默重试 3 次,采用指数退避(exponential backoff)延迟,全部重试失败后才把错误暴露给 UI。

指数退避的实现在 packages/query-core/src/retryer.ts:

function defaultRetryDelay(failureCount: number) { return Math.min(1000 * 2 ** failureCount, 30000) }

即第 1 次失败后等待约 2 秒(2^1),第 2 次约 4 秒,依次递增,并封顶 30 秒。若想改变,可覆盖retryretryDelay两个选项:

  • retry:可设为false(不重试)、数字(重试次数)或(failureCount, error) => boolean回调;
  • retryDelay:可设为固定毫秒数,或(failureCount, error) => number自定义延迟函数,实现线性退避、抖动(jitter)等策略。

补充:queryClient层面还提供缓存型默认值。queryClient.setQueryDefaults()会为特定queryKey注册默认选项,例如"对所有posts键的查询默认重试 5 次";这些键级默认值会在defaultQueryOptions()中与全局默认、查询自身选项按优先级合并(见下节)。

默认六:结构共享(Structural Sharing)保持引用稳定

查询结果默认启用结构共享(structural sharing):库会检测新数据与旧数据是否"实质变化",若未变化则保持原数据引用不变。这有助于配合useMemouseCallback做值稳定化,减少不必要的重渲染。初次接触该概念不必焦虑——绝大多数场景下无需关闭它,它几乎以零成本提升应用性能。

需要留意的边界与扩展:

  • 结构共享仅对 JSON 兼容的值有效,其他值类型(如Date、自定义类实例、Map/Set 等)永远被视为已变化;
  • 若因响应体巨大等原因出现性能问题,可通过structuralSharing: false关闭该特性(类型定义见 packages/query-core/src/types.ts);
  • 若响应中存在非 JSON 兼容值,又希望正确判断是否变化,可以传入自定义函数作为structuralSharing,由它基于新旧响应计算并保留所需引用。

如何在全局与单查询层面覆盖默认值

无论是staleTimegcTimeretry还是structuralSharing,配置都有三层合并优先级,最终由 queryClient.ts 的defaultQueryOptions()统一执行:

const defaultedOptions = { ...this.#defaultOptions.queries, // ① QueryClient 全局默认 ...this.getQueryDefaults(options.queryKey), // ② 按 queryKey 的默认 ...options, // ③ 本次查询显式传入的选项 _defaulted: true, }

全局配置示例

import { QueryClient } from '@tanstack/preact-query' export const queryClient = new QueryClient({ defaultOptions: { queries: { staleTime: 5 * 60 * 1000, // 5 分钟内视为新鲜,不重复请求 gcTime: 30 * 60 * 1000, // 闲置 30 分钟后回收 retry: 2, // 失败重试 2 次 refetchOnWindowFocus: false, structuralSharing: true, }, }, })

按查询配置示例

import { useQuery } from '@tanstack/preact-query' // 该查询独享 2 分钟新鲜期,且失败不重试 const { data } = useQuery({ queryKey: ['posts', id], queryFn: () => fetchPost(id), staleTime: 2 * 60 * 1000, retry: false, }) // 只读的静态配置,永不过期、无法被失效 const { data: flags } = useQuery({ queryKey: ['feature-flags'], queryFn: () => fetchFlags(), staleTime: 'static', })

useQuery外,useInfiniteQueryuseSuspenseQuery以及@tanstack/preact-query导出的queryOptions/infiniteQueryOptions帮助函数均支持同样的选项结构,可查看 packages/preact-query/src/index.ts 的完整导出清单。

默认值速查对照表

默认行为默认值/时机相关选项如何调整
缓存数据一经获取即视为 stalestaleTime: 0staleTime设为毫秒数、Infinity'static'
挂载新实例时后台刷新stale 即触发refetchOnMountfalse/'always'
窗口聚焦时后台刷新stale 即触发refetchOnWindowFocusfalse/'always'
网络重连时后台刷新stale 即触发refetchOnReconnectfalse/'always',默认受networkMode联动
定时轮询不开启refetchInterval毫秒间隔(与staleTime独立)
闲置查询回收inactive 后5 分钟gcTime其他毫秒值;SSR 下默认为Infinity
失败静默重试3 次,指数退避retry/retryDelay次数、布尔或自定义策略函数
结构共享默认开启structuralSharingfalse或自定义比较函数

默认值对调试心态的启示

文档在开头特别强调:这些默认值会在用户不知情时让学习与调试变难。实践中两个高频"惊吓点"分别是:

  1. "我没有写 refetch 代码,为什么页面一聚焦数据就变了?"——实为refetchOnWindowFocus配合默认staleTime: 0在起作用;
  2. "请求明明失败了,为什么 UI 迟迟不报错?"——实为默认重试 3 次 + 指数退避在按 2s/4s/8s 静默重试。

理解了本文的默认值体系后,这两类现象都可被准确预判。关于缓存失效与手动刷新之间的关系,可继续阅读 Query Invalidation 指南 与 Caching 指南 获取完整图景。该指南页面本身由官方文档生成机制从 React 版源文档 派生,社区深入讨论还可参见 Community Resources。

【免费下载链接】query🤖 Powerful asynchronous state management, server-state utilities and data fetching for the web. TS/JS, React Query, Solid Query, Svelte Query and Vue Query.项目地址: https://gitcode.com/GitHub_Trending/qu/query

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/10 4:34:33

嵌入式面试四大实战能力:硬件感知、资源博弈、系统穿透与现场还原

1. 这不是技术筛选,是嵌入式工程师的“现场压力测试”“面试老是挂”——这句话我听过不下两百遍,几乎每个来跟我聊职业发展的嵌入式新人,开口第一句就是这个。但真正让我警觉的,不是他们挂了,而是他们挂完之后说的下一…

作者头像 李华
网站建设 2026/9/10 4:34:21

零基础深度学习入门实战路线图:从NumPy感知机到PyTorch/TensorFlow

1. 这不是“速成课”,而是一张你真正能走通的深度学习入门地图我带过不下两百个零基础转行做算法的同学,也给高校本科生讲过三年《机器学习导论》。每次开课前,我都会问一个问题:“你手头有没有跑通过哪怕一个最简单的神经网络&am…

作者头像 李华
网站建设 2026/9/10 4:33:45

多无人机协同运输的Matlab仿真:路径规划与动态控制全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/10 4:32:12

Redis 6.2.6在Linux服务器上的源码编译与Docker部署指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华