尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

TanStack Query 的 QueryClient 完全指南:缓存交互、数据获取与状态管理全方法详解

TanStack Query 的 QueryClient 完全指南:缓存交互、数据获取与状态管理全方法详解 TanStack Query 的 QueryClient 完全指南缓存交互、数据获取与状态管理全方法详解【免费下载链接】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/queryQueryClient是 TanStack QueryReact Query / Solid Query / Svelte Query / Vue Query 等中与缓存交互的统一入口负责查询与变更mutation的获取、缓存、失效、刷新与清理。本文以 docs/reference/QueryClient.md 为核心结合 query-core 的源码实现与测试用例系统讲解QueryClient的全部方法从query/infiniteQuery的命令式数据获取到getQueryData/setQueryData的同步读写再到invalidateQueries/refetchQueries等缓存治理手段。读完本文你将掌握在事件回调、路由守卫、乐观更新、SSR 预取等场景中正确、安全地使用QueryClient的完整能力。一、认识 QueryClient与缓存交互的枢纽QueryClient的核心职责是与缓存交互。它内部持有一个QueryCache查询缓存和一个MutationCache变更缓存并对外暴露统一 API。从源码看它的私有字段包括两个缓存、默认选项、查询默认项与变更默认项的Map、以及挂载计数等见 queryClient.ts#L61-L78。最简单的创建方式import { QueryClient } from tanstack/react-query const queryClient new QueryClient({ defaultOptions: { queries: { staleTime: Infinity, }, }, }) await queryClient.query({ queryKey: [posts], queryFn: fetchPosts })构造选项QueryClientConfig构造函数接受一个可选的配置对象字段定义见 types.ts#L1363-L1367queryCache?: QueryCache可选该客户端连接的查询缓存。不传时内部会自动new QueryCache()。mutationCache?: MutationCache可选该客户端连接的变更缓存。defaultOptions?: DefaultOptions可选为该客户端下所有查询和变更定义默认选项同时可定义 hydration水合 时使用的默认值hydrate/dehydrate见 types.ts#L1369-L1377。构造函数中若未传入缓存会各自创建新实例未传入默认选项则初始化为空对象随后在defaultQueryOptions中与查询级默认值、具体选项按优先级合并详见下文默认选项合并。方法总览QueryClient提供的方法如下本文将逐一展开数据获取query、infiniteQuery数据读取getQueryData、getQueriesData、getQueryState数据写入setQueryData、setQueriesData缓存治理invalidateQueries、refetchQueries、cancelQueries、removeQueries、resetQueries状态统计isFetching、isMutating默认选项getDefaultOptions、setDefaultOptions、getQueryDefaults、setQueryDefaults、getMutationDefaults、setMutationDefaults缓存访问与清理getQueryCache、getMutationCache、clear、resumePausedMutations二、命令式数据获取query 与 infiniteQueryqueryClient.queryquery是一个异步方法用于获取并缓存一个查询。它要么 resolve 出数据要么 throw 出错误。如果查询已存在且数据未被失效、也未超过给定的staleTime则直接返回缓存数据否则尝试拉取最新数据。对应实现见 queryClient.ts#L346-L387通过query.isStaleByTime(resolveQueryValue(staleTime, query))判断缓存是否过期过期则await query.fetch(defaultedOptions)否则直接返回query.state.data。try { const data await queryClient.query({ queryKey, queryFn }) } catch (error) { console.log(error) }指定staleTime仅在数据超过指定时间后才触发请求同时可用select对返回数据做投影try { const data await queryClient.query({ queryKey, queryFn, staleTime: 10000, select: (data) data.items, }) } catch (error) { console.log(error) }Optionsquery的选项与useQuery完全相同但排除以下仅用于useQuery/useInfiniteQuery的项enabled、refetchInterval、refetchIntervalInBackground、refetchOnWindowFocus、refetchOnReconnect、refetchOnMount、notifyOnChangeProps、throwOnError、suspense、placeholderData。类型层面由 QueryExecuteOptions 约束WithRequiredQueryOptions, queryKeyinitialPageParam被限定为never。ReturnsPromiseTData。实现细节注意源码中query与已废弃的fetchQuery都有一段retry undefined时强制retry false的逻辑queryClient.ts#L366-L368即命令式query调用默认不重试需要重试时请显式传retry选项。测试 queryClient.test.tsx 中的 should not retry by default 与 should return the cached data on cache hit 等用例验证了这一行为。queryClient.infiniteQueryinfiniteQuery与query类似但用于获取并缓存无限查询分页场景try { const data await queryClient.infiniteQuery({ queryKey, queryFn }) console.log(data.pages) } catch (error) { console.log(error) }OptionsinfiniteQuery的选项与query完全相同额外增加来自useInfiniteQuery的initialPageParam、pages结合getNextPageParam使用等。类型层面由 InfiniteQueryExecuteOptions 定义它去掉query的initialPageParam再并入InitialPageParamTPageParam与InfiniteQueryPages即pages与getNextPageParam成对出现。实现上infiniteQuery内部只是给options打上_type: infinite标记后委托给query见 queryClient.ts#L437-L458。ReturnsPromiseInfiniteDataTData, TPageParam当TData为InfiniteData时。三、同步读取缓存getQueryData、getQueriesData、getQueryStatequeryClient.getQueryData同步获取某个 queryKey 对应的缓存数据若查询不存在则返回undefined。源码通过defaultQueryOptions({ queryKey })计算出queryHash再从queryCache中取出state.dataqueryClient.ts#L129-L138。const data queryClient.getQueryData([posts, 1])注意getQueryData是非响应式的读取只适合在回调函数或需要读取最新值的场景如乐观更新中使用不要在组件渲染中调用它组件内请使用useQuery它会创建订阅变更的QueryObserver。这正是 queryClient.ts 中对该方法的注释所强调的。queryClient.getQueriesData同步获取多个查询的缓存数据只返回匹配传入queryKey或查询过滤器queryFilter的查询没有匹配时返回空数组。const data queryClient.getQueriesData(filters)Optionsfilters: QueryFilters即 Query Filters传入过滤器后仅返回 queryKey 匹配的数据。Returns[queryKey: QueryKey, data: TQueryFnData | undefined][]即匹配查询键与对应数据组成的元组数组无匹配时为[]。Caveats由于每个元组中返回的数据结构可能不同例如用过滤器匹配 active 查询可能返回不同类型的数据TData泛型默认是unknown。只有当你确信每个元组的数据都是同一类型时才应提供更具体的泛型——这是为熟悉返回结构的 TS 开发者提供的便利。queryClient.getQueryState同步获取某个已存在查询的状态对象查询不存在时返回undefined。例如读取数据更新时间const state queryClient.getQueryState(queryKey) console.log(state.dataUpdatedAt)OptionsqueryKey: QueryKey详见 Query Keys。实现细节getQueryState与getQueryData一样走defaultQueryOptions→queryCache.get(queryHash)的链路只是返回的是整个QueryState含data、dataUpdatedAt、status、fetchStatus等见 queryClient.ts#L235-L248。四、写入缓存setQueryData 与 setQueriesDataqueryClient.setQueryData同步函数用于立即更新某个查询的缓存数据。若查询不存在它会被创建如果该查询在默认gcTime内没有被任何查询 hook 使用则会被垃圾回收默认gcTime未配置时为 5 分钟。需要一次更新多个查询或对 queryKey 做部分匹配时应改用setQueriesData。setQueryData 与 query 的区别setQueryData是同步的它假定你已经同步拿到了数据。如果你需要异步获取数据建议直接 refetch 该 queryKey或使用query来接管异步获取。queryClient.setQueryData(queryKey, updater)OptionsqueryKey: QueryKey见 Query Keysupdater: TQueryFnData | undefined | ((oldData: TQueryFnData | undefined) TQueryFnData | undefined)传入非函数值数据被更新为该值传入函数接收旧数据并返回新数据。使用 updater 值setQueryData(queryKey, newData)如果值是undefined查询数据不会被更新。使用 updater 函数setQueryData(queryKey, (oldData) newData)如果 updater 函数返回undefined查询数据不会被更新如果 updater 函数接收到的输入是undefined即旧数据不存在你同样可以返回undefined来退出更新从而不创建新的缓存条目。源码中functionalUpdate(updater, prevData)后对data undefined直接return undefined的逻辑印证了这一点queryClient.ts#L202-L212对应的测试 should not create a new query if query was not found and updater returns undefinedqueryClient.test.tsx也验证了该行为。不可变性Immutability通过setQueryData更新必须采用不可变方式。禁止直接原地修改oldData或通过getQueryData取回的数据来写缓存——这会导致缓存无法正确触发订阅与比较。queryClient.setQueriesData同步更新多个查询的缓存数据支持通过过滤器或部分匹配 queryKey。只更新已存在的匹配查询不会创建新的缓存条目底层对每个已存在查询逐个调用setQueryData见 queryClient.ts#L214-L233且整体包在notifyManager.batch中批量通知。queryClient.setQueriesData(filters, updater)Optionsfilters: QueryFilters传入过滤器后匹配的 queryKey 会被更新见 Query Filters。updater: TQueryFnData | (oldData: TQueryFnData | undefined) TQueryFnDatasetQueryData的 updater 函数或新数据会对每个匹配 queryKey 调用。五、缓存治理invalidateQueries、refetchQueriesqueryClient.invalidateQueries用于基于 queryKey 或查询的任意可访问属性/状态失效并重新获取缓存中的单个或多个查询。默认情况下所有匹配查询会被立即标记为无效且active 查询会在后台重新获取。如果不想让 active 查询重新获取、只标记为无效使用refetchType: none如果希望 inactive 查询也重新获取使用refetchType: all重新获取环节内部调用的是refetchQueries。await queryClient.invalidateQueries( { queryKey: [posts], exact, refetchType: active, }, { throwOnError, cancelRefetch }, )Optionsfilters?: QueryFilters见 Query FiltersqueryKey?: QueryKey见 Query KeysrefetchType?: active | inactive | all | none默认activeactive只后台重取那些匹配谓词、且正被useQuery等渲染的查询inactive只后台重取匹配谓词、且未被useQuery等渲染的查询all重取所有匹配谓词的查询none不重取任何查询只把匹配的查询标记为无效。options?: InvalidateOptionsthrowOnError?: boolean设为true时若任一查询的重取任务失败该方法会抛出错误。cancelRefetch?: boolean默认true——默认会先取消当前正在进行的请求再发起新请求设为false时若已有请求在运行则不再重取。Notes与refetchQueries不同invalidateQueries会先标记匹配查询为失效再重取 active 查询除非用refetchType另行指定。与removeQueries不同invalidateQueries会把匹配查询保留在缓存中。实现细节从源码看queryClient.ts#L298-L318invalidateQueries先对匹配查询逐个调用query.invalidate()标记失效若refetchType为none则直接 resolve否则把refetchType或type映射为refetchQueries的type默认active后委托给它。queryClient.refetchQueries基于特定条件重取查询。注意与invalidateQueries不同refetchQueries不会标记失效只是重取所有匹配查询。示例// 重取所有查询 await queryClient.refetchQueries() // 重取所有 stale 查询 await queryClient.refetchQueries({ stale: true }) // 重取所有部分匹配 queryKey 的 active 查询 await queryClient.refetchQueries({ queryKey: [posts], type: active }) // 重取所有精确匹配 queryKey 的 active 查询 await queryClient.refetchQueries({ queryKey: [posts, 1], type: active, exact: true, })Optionsfilters?: QueryFilters见 Query Filtersoptions?: RefetchOptionsthrowOnError?: boolean设为true时若任一查询的重取任务失败则抛出错误。cancelRefetch?: boolean默认true——默认先取消进行中的请求再发起新请求设为false时已有请求运行时不再重取。Returns返回一个 Promise在所有查询重取完成后 resolve。默认不会因某个查询重取失败而抛出可通过throwOnError: true改变。Notes只拥有 disabled Observer 的 disabled 查询永远不会被重取只拥有静态 staleTime Observer 的 static 查询永远不会被重取与invalidateQueries不同refetchQueries会重取所有匹配查询。实现细节源码在 queryClient.ts#L320-L344。它默认cancelRefetch: true在notifyManager.batch中找出匹配查询过滤掉query.isDisabled()与query.isStatic()的查询这正是上面 Notes 中两条规则的来源然后逐个query.fetch()除非throwOnError否则用.catch(noop)吞掉错误最后Promise.all(...).then(noop)统一返回。六、取消、移除与重置cancelQueries、removeQueries、resetQueriesqueryClient.cancelQueries基于 queryKey 或查询的任意可访问属性/状态取消进行中的查询请求。这在做乐观更新时最有用——你需要取消进行中的查询重取避免它们 resolve 时覆盖掉你的乐观更新结果。await queryClient.cancelQueries( { queryKey: [posts], exact: true }, { silent: true }, )Optionsfilters?: QueryFilters见 Query FilterscancelOptions?: CancelOptions即 Cancel Options类型定义见 types.ts#L1379-L1382含revert?: boolean默认由源码补为true与silent?: boolean。Returns无返回值Promise 。实现细节源码queryClient.ts#L283-L296先合并默认revert: true再在 batch 中对匹配查询逐个调用query.cancel()并以Promise.all(...).then(noop).catch(noop)收尾因此即使在取消过程中出错也不会向上抛出。queryClient.removeQueries基于 queryKey 或查询属性/状态将查询从缓存中移除。queryClient.removeQueries({ queryKey, exact: true })Optionsfilters?: QueryFilters。Returns无返回值。Notes与invalidateQueries或refetchQueries不同removeQueries是把匹配查询从缓存中删除而不是重取。实现细节源码在 batch 中先findAll(filters)再逐个queryCache.remove(query)queryClient.ts#L250-L259。测试 should remove query when exact is truequeryClient.test.tsx等用例覆盖了该行为。queryClient.resetQueries基于 queryKey 或查询属性/状态把缓存中的查询重置到初始状态。它会通知订阅者与clear移除所有订阅者不同并把查询重置到预加载前的状态与invalidateQueries不同。若查询配置了initialData数据会被重置为该值若查询是 active 的会被重新获取。queryClient.resetQueries({ queryKey, exact: true })Optionsfilters?: QueryFilters见 Query Filtersoptions?: ResetOptionsthrowOnError?: boolean设为true时任一查询的重取任务失败则抛出。cancelRefetch?: boolean默认true先取消再重取false时已有请求运行则不再重取。Returns返回一个在所有 active 查询重取完成后 resolve 的 Promise。实现细节源码queryClient.ts#L261-L281先对匹配查询逐个调用query.reset()再以type: active 匹配查询集合作为predicate调用refetchQueries。七、状态统计isFetching 与 isMutatingqueryClient.isFetching返回一个整数表示缓存中当前正在获取的查询数量包括后台获取、加载新页、加载更多无限查询结果等。if (queryClient.isFetching()) { console.log(At least one query is fetching!) }TanStack Query 还导出了便捷的useIsFetchinghook让你在组件中订阅该状态而无需手动订阅查询缓存。Optionsfilters?: QueryFilters。Returns正在获取的查询数量。实现细节源码为queryCache.findAll({ ...filters, fetchStatus: fetching }).lengthqueryClient.ts#L109-L114。queryClient.isMutating返回一个整数表示缓存中当前正在执行的变更mutation数量。if (queryClient.isMutating()) { console.log(At least one mutation is fetching!) }同样有对应的useIsMutatinghook 可在组件中订阅。Optionsfilters: MutationFilters见 Mutation Filters。Returns正在执行的变更数量。实现细节源码为mutationCache.findAll({ ...filters, status: pending }).lengthqueryClient.ts#L116-L120。八、默认选项全局与按 key 定制QueryClient提供三档默认选项构造时的全局默认defaultOptions、按 queryKey 的查询默认项、按 mutationKey 的变更默认项。它们最终在defaultQueryOptions/defaultMutationOptions中合并。getDefaultOptions / setDefaultOptionsgetDefaultOptions返回创建客户端时或通过setDefaultOptions设置的默认选项const defaultOptions queryClient.getDefaultOptions()setDefaultOptions用于动态设置该客户端的默认选项之前定义的默认选项会被覆盖queryClient.setDefaultOptions({ queries: { staleTime: Infinity, }, })实现见 queryClient.ts#L541-L547getDefaultOptions直接返回内部#defaultOptionssetDefaultOptions整体替换。getQueryDefaults / setQueryDefaultsgetQueryDefaults返回为特定查询设置的默认选项const defaultOptions queryClient.getQueryDefaults([posts])注意如果多个查询默认项都匹配给定的 queryKey它们会按注册顺序合并。参见下面的setQueryDefaults。setQueryDefaults为特定查询设置默认选项queryClient.setQueryDefaults([posts], { queryFn: fetchPosts }) function Component() { const { data } useQuery({ queryKey: [posts] }) }OptionsqueryKey: QueryKey见 Query Keysoptions: QueryOptions如getQueryDefaults中所述查询默认项的注册顺序很重要。由于匹配的默认项会通过getQueryDefaults合并注册顺序应遵循从最通用的 key 到最不通用最具体的 key。这样更具体的默认项会覆盖更通用的默认项。实现细节setQueryDefaults以hashKey(queryKey)为键存入MapqueryClient.ts#L549-L567getQueryDefaults遍历所有注册项用partialMatchKey(queryKey, queryDefault.queryKey)实现见 utils.ts#L239-L240即数组前缀匹配判断是否命中并按注册顺序Object.assign合并因此先注册的通用项被后注册的具体项覆盖。setQueryDefaults的测试 should match the query key partially 与 should not match if the query key is a subsetqueryClient.test.tsx验证了部分匹配语义。getMutationDefaults / setMutationDefaultsgetMutationDefaults返回为特定变更设置的默认选项const defaultOptions queryClient.getMutationDefaults([addPost])setMutationDefaults为特定变更设置默认选项queryClient.setMutationDefaults([addPost], { mutationFn: addPost }) function Component() { const { data } useMutation({ mutationKey: [addPost] }) }OptionsmutationKey: unknown[]options: MutationOptions与setQueryDefaults类似这里的注册顺序同样重要。默认选项的合并链路从源码defaultQueryOptionsqueryClient.ts#L624-L703可以看出合并优先级由低到高全局#defaultOptions.queries→getQueryDefaults(queryKey)→ 本次传入的options。此外它还会补全依赖性的默认值refetchOnReconnect默认取networkMode ! alwaysthrowOnError默认取!!suspense存在persister时networkMode默认offlineFirstqueryFn skipToken时enabled强制为false。defaultMutationOptionsqueryClient.ts#L705-L718的合并链路同理全局#defaultOptions.mutations→getMutationDefaults(mutationKey)→ 本次传入选项。九、缓存访问、清理与断线恢复getQueryCache / getMutationCache分别返回该客户端连接的查询缓存与变更缓存const queryCache queryClient.getQueryCache() const mutationCache queryClient.getMutationCache()这两个方法只是直接返回内部持有的#queryCache/#mutationCachequeryClient.ts#L533-L539。拿到缓存后可以进一步使用 QueryCache 或 MutationCache 的订阅、查找等能力。queryClient.clear清空所有连接的缓存查询缓存与变更缓存queryClient.clear()实现为同时调用#queryCache.clear()与#mutationCache.clear()queryClient.ts#L720-L723。queryClient.resumePausedMutations用于恢复那些因没有网络连接而被暂停的变更queryClient.resumePausedMutations()实现细节源码queryClient.ts#L526-L531先检查onlineManager.isOnline()在线才调用#mutationCache.resumePausedMutations()否则直接 resolve。该能力与网络状态管理紧密关联QueryClient.mount()queryClient.ts#L80-L96在应用挂载时会订阅focusManager与onlineManager一旦窗口重新聚焦或网络恢复都会先resumePausedMutations()再分别触发queryCache.onFocus()/queryCache.onOnline()。测试 queryClient.test.tsx 中 should resumePausedMutations when coming online after having called resumePausedMutations while offline 等用例覆盖了离线→在线时的恢复链路。十、实战小结与选型建议结合文档与源码QueryClient的典型使用场景可以归纳为场景推荐方法关键注意点事件回调/路由守卫中获取数据query/infiniteQuery默认不重试命中未过期缓存时不发请求乐观更新前取旧数据getQueryData非响应式勿在组件渲染中使用乐观更新写缓存setQueryData必须不可变更新undefined不更新不建条目批量更新多个查询setQueriesData只更新已存在查询不新建条目数据变更后刷新invalidateQueries默认只重取 active 查询refetchType: none只标记失效无条件强制重取refetchQueriesdisabled / static 查询不会被重取防止旧响应覆盖乐观更新cancelQueries默认revert: true注销/退出时清数据removeQueries/clear前者按过滤器删除后者清空全部恢复初始状态resetQueries会通知订阅者并重取 active 查询全局统一配置setDefaultOptions整体覆盖之前定义的默认项按 key 定制默认项setQueryDefaults/setMutationDefaults注册顺序从最通用到最具体断线重连后恢复resumePausedMutations聚焦/上线时自动触发进一步深入可参考核心实现 queryClient.ts 与类型定义 types.ts行为验证见测试 queryClient.test.tsx概念背景可阅读 Query Filters、Query Keys、Query Cancellation 与 SSR 指南。【免费下载链接】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),仅供参考
返回列表