TanStack Query FocusManager 完全指南:掌控焦点状态与窗口重聚焦时的数据刷新

发布时间:2026/9/10 23:59:43

TanStack Query FocusManager 完全指南:掌控焦点状态与窗口重聚焦时的数据刷新 TanStack Query FocusManager 完全指南掌控焦点状态与窗口重聚焦时的数据刷新【免费下载链接】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导读FocusManager是 TanStack Query 核心层query-core中负责统一管理窗口/页面焦点状态的单例管理器它决定了查询在用户切换到其他标签页再返回时是否自动重新获取数据。本文将以官方参考文档为主体结合仓库源码深入讲解FocusManager的四个核心方法setEventListener、subscribe、setFocused、isFocused剖析它如何接入浏览器的visibilitychange事件、如何与重试机制retryer、QueryClient挂载生命周期及refetchInterval定时刷新协同工作并给出覆盖 React、Vue、Svelte 等框架的实战用法与测试验证。读完本文你将能完全掌控 TanStack Query 的窗口焦点感知行为并能针对特定框架或业务场景自定义焦点事件源。一、FocusManager 是什么FocusManager在 TanStack Query 中负责管理焦点状态focus state。默认情况下TanStack Query 会在浏览器窗口重新获得焦点例如用户切走标签页后再切回来时自动重新获取refetch那些处于 stale 状态的查询——这一魔法行为的底层就是FocusManager。它的两大职责来自官方文档改变默认的事件监听器用于适配 React Native、Web 之外的运行环境或自定义事件源手动改变焦点状态用于在自动化测试、或需要人为控制刷新时机时直接注入状态。官方文档给出的可用方法共四个全部可直接通过框架包导出的单例调用setEventListenersubscribesetFocusedisFocused在仓库中FocusManager类位于 packages/query-core/src/focusManager.ts并通过export const focusManager new FocusManager()导出一个全局单例同时从 packages/query-core/src/index.ts 对外导出。各框架包如react-query、vue-query、svelte-query均从tanstack/query-core再导出该单例因此无论你使用哪个框架都可以这样导入import { focusManager } from tanstack/react-query // Vue / Svelte / Solid 等其他框架同理 // import { focusManager } from tanstack/vue-query // import { focusManager } from tanstack/svelte-query // import { focusManager } from tanstack/solid-query二、focusManager.setEventListener自定义焦点事件源2.1 官方用法setEventListener用于设置自定义的事件监听器。当默认的visibilitychange监听不适用于你的运行环境时例如 React Native、Electron、小程序容器可以用它替换掉默认实现import { focusManager } from tanstack/react-query focusManager.setEventListener((handleFocus) { // Listen to visibilitychange if (typeof window ! undefined window.addEventListener) { window.addEventListener(visibilitychange, handleFocus, false) } return () { // Be sure to unsubscribe if a new handler is set window.removeEventListener(visibilitychange, handleFocus) } })setEventListener接收一个setup函数TanStack Query 会把一个handleFocus回调传给它setup 函数负责把该回调挂到任意事件源上并返回一个清理函数用于在新监听器替换或订阅全部取消时解除绑定。2.2 源码级解析替换与清理机制从源码 packages/query-core/src/focusManager.ts 可以看到setEventListener的实现setEventListener(setup: SetupFn): void { this.#setup setup this.#cleanup?.() this.#cleanup setup((focused) { if (typeof focused boolean) { this.setFocused(focused) } else { this.onFocus() } }) }三个关键点先清理再替换每次调用setEventListener都会先执行上一次 setup 返回的#cleanup确保旧的visibilitychange监听被移除不会发生事件泄漏或重复触发回调参数可带值可空传入 setup 的handleFocus既可以接收boolean直接设置焦点状态也可以无参调用走默认的焦点判定逻辑onFocus()默认 setup在FocusManager构造函数中focusManager.ts内置了默认 setup——当typeof window ! undefined window.addEventListener时监听visibilitychange事件并返回对应的removeEventListener清理函数。值得注意的是源码注释明确说明addEventListener在 React Native 中不存在但window存在因此该守卫是必须的。测试 packages/query-core/src/tests/focusManager.test.tsx 中验证了这些行为should call previous remove handler when replacing an event listener连续两次setEventListener第一次的 remove 函数会被调用一次第二次的不会被调用——印证了替换即清理cleanup (removeEventListener) should not be called if window is not defined 与 …if window.addEventListener is not defined在window不存在或window.addEventListener不存在时取消订阅不会调用removeEventListener印证了默认 setup 的环境守卫逻辑。2.3 实战React Native 自定义事件源在 React Native 中标准的visibilitychange并不存在社区常用AppStateAPI 来感知前后台切换。你可以这样接入import { AppState } from react-native import { focusManager } from tanstack/react-query focusManager.setEventListener((handleFocus) { const subscription AppState.addEventListener(change, (status) { handleFocus(status active) }) return () subscription.remove() })这样当 App 从后台回到前台active时TanStack Query 就会像 Web 端切回标签页一样触发焦点恢复逻辑。三、focusManager.subscribe订阅焦点状态变化subscribe用于订阅可见性/焦点状态的变化并返回一个取消订阅的函数官方文档强调It returns an unsubscribe functionimport { focusManager } from tanstack/react-query const unsubscribe focusManager.subscribe((isVisible) { console.log(isVisible, isVisible) }) // 不再需要时取消订阅 unsubscribe()3.1 底层机制继承自 SubscribableFocusManager继承自 packages/query-core/src/subscribable.ts 中的Subscribable基类subscribe(listener)将监听器加入Set触发onSubscribe()返回一个删除监听器并触发onUnsubscribe()的取消函数FocusManager覆写了onSubscribe/onUnsubscribefocusManager.ts当第一个订阅者出现时才通过setEventListener(this.#setup)挂载事件监听当最后一个订阅者取消后执行#cleanup并置空实现按需挂载、零订阅零开销。测试 should call removeEventListener when last listener unsubscribesfocusManager.test.tsx验证了两个订阅者只注册一次visibilitychange第二个取消订阅时才真正移除监听。3.2 谁在订阅QueryClient 挂载时接入QueryClient.mount()packages/query-core/src/queryClient.ts正是通过subscribe与焦点状态联动的mount(): void { this.#mountCount if (this.#mountCount ! 1) return this.#unsubscribeFocus focusManager.subscribe(async (focused) { if (focused) { await this.resumePausedMutations() this.#queryCache.onFocus() } }) // ... }即窗口重新获得焦点时QueryClient会先恢复被暂停的 mutation再通知QueryCache对所有受影响的查询执行onFocus刷新queryCache.onFocus()会调用查询的onFocus()以重新获取数据。这也解释了为何默认行为是切回标签页自动刷新。当unmount()且挂载计数归零时对应订阅会被取消避免内存泄漏。四、focusManager.setFocused手动控制焦点状态setFocused用于手动设置焦点状态。传undefined时则回退到默认的焦点检查逻辑即基于document.visibilityState的判定import { focusManager } from tanstack/react-query // Set focused focusManager.setFocused(true) // Set unfocused focusManager.setFocused(false) // Fallback to the default focus check focusManager.setFocused(undefined)Optionsfocused: boolean | undefined4.1 源码行为变更才通知从源码 focusManager.ts 可以看到setFocused(focused?: boolean): void { const changed this.#focused ! focused if (changed) { this.#focused focused this.onFocus() } }只有状态实际发生变化时才会广播给所有监听者连续设置相同的值不会触发通知。测试 should call listeners when setFocused is calledfocusManager.test.tsx精确验证了这一点连续两次setFocused(true)只通知一次随后setFocused(undefined)会回退到默认判定测试环境中document被模拟为可见因此回调收到true。4.2 实战场景测试场景在 Vitest/Jest 中模拟窗口失焦/聚焦无需真实触发浏览器事件// 模拟失焦让重试与定时刷新暂停 focusManager.setFocused(false) // 恢复聚焦 focusManager.setFocused(true)后台预取场景某些场景下你希望在页面名义上失焦时仍不中断查询或反过来强制让查询感知到焦点均可通过此 API 精确控制。五、focusManager.isFocused读取当前焦点状态isFocused返回当前焦点状态booleanconst isFocused focusManager.isFocused()5.1 默认判定逻辑源码 focusManager.ts 的默认实现isFocused(): boolean { if (typeof this.#focused boolean) { return this.#focused } // document global can be unavailable in react native return globalThis.document?.visibilityState ! hidden }若此前通过setFocused(boolean)手动设置过则直接返回该值否则基于globalThis.document?.visibilityState ! hidden判定即只要页面不是hidden状态就算聚焦。源码注释特别说明document全局对象在 React Native 中可能不存在因此使用了可选链此时判定为true不会误判为失焦。测试 should return true for isFocused if document is undefinedfocusManager.test.tsx专门验证了删除globalThis.document后isFocused()返回true的边界行为。六、FocusManager 在数据获取链路中的关键作用FocusManager 并非孤立存在它在查询生命周期中承担着节流阀的角色直接影响数据获取的启停。6.1 重试暂停retryer.ts查询的获取与重试由 packages/query-core/src/retryer.ts 中的createRetryer驱动。其canContinue判定retryer.ts为const canContinue () focusManager.isFocused() (config.networkMode always || onlineManager.isOnline()) config.canRun()当查询失败进入重试等待时如果窗口失焦或设备离线run循环会调用pause()挂起重试源码注释明确写着 Pause if the document is not visible or when the device is offline一旦重新聚焦focusManager状态变化等待中的重试继续执行。这正是失焦时停止无谓请求、聚焦时无缝续传的核心机制。6.2 定时刷新守卫queryObserver.ts在QueryObserver的定时重取逻辑中packages/query-core/src/queryObserver.tsrefetchInterval的每次触发都会检查焦点this.#refetchIntervalId timeoutManager.setInterval(() { if ( this.options.refetchIntervalInBackground || focusManager.isFocused() ) { this.#executeFetch() } }, this.#currentRefetchInterval)即默认情况下窗口失焦时定时刷新被跳过只有显式设置refetchIntervalInBackground: true才在后台继续刷新。6.3 焦点恢复联动queryClient.tsqueryCache.ts如 3.2 节所述QueryClient.mount()订阅焦点变化后调用queryCache.onFocus()packages/query-core/src/queryCache.ts后者遍历所有查询触发各自的query.onFocus()完成回到页面即刷新 stale 数据的默认行为。七、与在线状态管理器的对比FocusManager与OnlineManager结构对称、职责互补FocusManager感知焦点OnlineManager感知网络。二者都以单例形式存在于query-core都提供setEventListener/subscribe/setFocused(isOnline)/isFocused(isOnline)这类同构 API并且在retryer.ts的canContinue中被并列判断见 6.1 节。如果你需要为离线优先的应用自定义网络状态判定可参考对应的 onlineManager.md 文档其使用模式与本文完全一致。八、要点总结方法作用典型场景setEventListener(setup)替换默认事件监听器默认监听visibilitychangeReact Native / Electron / 自定义事件源subscribe(listener)订阅焦点状态变化返回取消函数与QueryClient.mount()联动、自定义副作用setFocused(boolean \| undefined)手动设置焦点状态undefined回退默认判定测试模拟、强制控制刷新isFocused()读取当前焦点状态重试暂停/恢复、定时刷新守卫核心要点回顾默认事件源是浏览器的visibilitychangefocusManager.ts且带window.addEventListener存在性守卫兼容 React Native替换即清理setEventListener会先执行旧监听器的清理函数杜绝事件泄漏懒挂载只有出现第一个订阅者才挂载事件监听最后一个取消订阅后立即清理subscribable.ts变更才通知setFocused仅在状态实际变化时广播focusManager.test.tsx三处核心消费方retryer的重试暂停retryer.ts、QueryClient.mount()的焦点恢复刷新queryClient.ts、QueryObserver的定时刷新守卫queryObserver.ts。无论你是要适配非浏览器环境、优化移动端体验还是要编写稳定可靠的测试FocusManager都是你必须掌握的 TanStack Query 核心基础设施。【免费下载链接】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),仅供参考
延伸阅读

更多相关文章

2026/9/10 23:54:43

K3S 基础命令集

K3S 基础命令集Pod查看 Pod查看所有Pod查看详情查看Pod 日志查看 IP查看yaml编辑配置删除Pod强制删除进入容器PVC查看所有PVC查看详情查看PVC YAML创建 PVC编辑 PVC删除 PVCService查看 Service查看 Service 详情查看 Service YAML创建 Service编辑 Service删除 Servicedepoly查…

2026/9/10 23:54:43

2027届论文降AI率平台哪个靠谱?六款实测横评

高校对AI生成内容的检测力度逐年加强,2027届毕业生面临的降AI率压力比往届更大。论文写作过程中适度使用AI辅助已成常态,但如何让最终稿顺利通过检测,成为不少学生头疼的问题。这篇测评选取市面上讨论度较高的六款降AI率工具,逐一…

2026/9/11 0:44:48

电机电磁场仿真核心:静磁场分析实操与避坑指南

做电机电磁场仿真这些年,我越来越觉得一个道理:如果你能把静磁场仿真做到位,电机的绝大多数设计问题都能在早期得到准确答案。静磁场仿真听起来像是电磁场分析里的“入门题型”,但在电机设计的真实场景中,它反而是用得…

2026/9/11 0:44:48

MongoDB安装与基础使用指南

1. MongoDB安装前的准备工作在开始安装MongoDB之前,我们需要先了解一些基本概念和准备工作。MongoDB是一个基于分布式文件存储的开源数据库系统,由C语言编写,旨在为WEB应用提供可扩展的高性能数据存储解决方案。1.1 系统环境检查首先确认你的…

2026/9/11 0:44:48

LoRA微调前必做的显存与训练时长估算指南

你有没有遇到过这种情况:项目启动前先被人问一句“这张卡能撑住这个模型的LoRA微调吗?大概要跑多久?”如果回答依赖的是“应该可以吧”和“先跑跑看”,那多半就要在几次OOM和漫长的等待中度过。反而是那些能按公式快速估算的人&am…

2026/9/11 0:39:47

论文降AIGC工具测评:原理、应用与避坑指南

1. 论文降AIGC工具测评背景与必要性 2023年ChatGPT的爆发式普及彻底改变了学术写作的生态格局。根据Nature最新调查显示,62%的研究者承认在日常学术工作中使用生成式AI工具辅助写作。但随之而来的学术诚信问题也引发全球教育界的广泛关注——全球TOP100高校中已有89…

2026/9/10 16:39:38

超人会飞不算本事:系统稳定依赖清晰规则与边界设计

开头先不绕弯子。“#斯坦李吐槽dc 所以超人是无缘无故会飞的嘛哈哈哈哈哈哈哈锤哥真是技术人才啊!#雷神 #复联”这类调侃式短标题,第一波冲击力在于它把两个宇宙的角色塞进同一个吐槽箱里,但细想一下就能发现,它真正碰到的根本不是…

2026/9/10 11:16:38

超人VS蜘蛛侠:拆解超级IP的影响力与传播方法论

把“蜘蛛侠 vs 超人”放在 CSDN 上聊,可能很多人第一反应是走错片场了。但如果把这两个角色看成“两个持续运营了 80 多年的文化产品”,你会发现,这场比较本质上是两个不同 IP 策略的长期结果对比:超人赢在定义了整个超级英雄题材…

2026/9/9 16:31:09

基于CNN的调制信号识别:MATLAB实现时频图分类实战

简介:本资源是一套面向通信工程与信号处理方向学习者、研究者的深度学习实践方案,聚焦调制信号自动检测与识别这一典型无线通信任务,解决传统方法依赖人工特征、低信噪比下性能下降等痛点。压缩包共12个文件(10.73MB)&…

2026/9/10 12:32:02

USB Type-C PCB布局分区设计:电源、高速信号与PD协议全攻略

做硬件这行,Type-C接口算是典型的“看着简单,做起来全坑”的东西。光引脚就24个,高低速信号、电源、控制线全部塞在一个小小的连接器里,如果PCB布局不做规划,打样回来基本就是“插上没反应”、“高速掉线”、“静电一打…

2026/9/10 15:19:50

系统编程学习原型如何补齐稳定性边界

系统编程学习原型如何补齐稳定性边界预算有限时&#xff0c;我先优化明显多余的复制&#xff0c;而不是猜测性地换容器。用借用传递只读数据通常就能减少分配&#xff1a; fn parse(line: &str) -> Result<Item, Error> { /* ... */ }用基准确认热点确实在分配&am…

2026/9/10 15:49:53

雨花区哪家财务公司代理记账比较好?

在雨花区&#xff0c;企业处理财税事务常常面临诸多挑战&#xff0c;选择一家靠谱的财务公司至关重要。湖南巨勤财务管理咨询有限公司就是本地正规实体财税服务机构&#xff0c;深耕本地工商财税行业多年&#xff0c;熟悉当地工商局、税务局最新政策与申报流程。主营公司注册、…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
咨询二维码