ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Vue Query 数据预取(Prefetching)完整指南:用 queryClient.query 与 infiniteQuery 预热缓存

Vue Query 数据预取(Prefetching)完整指南:用 queryClient.query 与 infiniteQuery 预热缓存 Vue Query 数据预取Prefetching完整指南用 queryClient.query 与 infiniteQuery 预热缓存【免费下载链接】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 Vue Query 应用中许多数据是用户操作之后才需要的例如详情页、下一页列表。本指南讲解如何利用queryClient.query与queryClient.infiniteQuery在数据被需要之前将其预热到缓存中涵盖staleTime、staleTime: static、垃圾回收、Infinite Query 多页预取等关键行为并结合仓库源码剖析底层实现。读完你将掌握一套可落地、可复用的预取方案显著提升页面切换与交互的响应速度。什么是预取Prefetching预取是指如果你对用户接下来的行为有足够把握例如用户进入列表页后大概率会点击第一条进入详情页就可以提前把“将来才需要”的数据请求发出去并写入查询缓存。这样当useQuery真正挂载时数据已经在缓存里组件可以立即渲染而不必等待网络往返。在 Vue Query 中完成预取的入口就是queryClient.query与queryClient.infiniteQuery对应 Infinite Query 场景。它们会把请求结果像普通查询一样写入缓存import { noop } from tanstack/vue-query const prefetchTodos async () { // The results of this query will be cached like a normal query await queryClient .query({ queryKey: [todos], queryFn: fetchTodos, }) .catch(noop) }提示在script setup或组件的setup()中queryClient通过const queryClient useQueryClient()获取其中useQueryClient由tanstack/vue-query导出示例参见 packages/vue-query/README.md。预取的核心行为规则理解预取关键是理解下面几条行为约定它们决定了缓存何时被命中、何时被重新拉取如果该查询在缓存中已有 fresh新鲜数据则不会再次发起请求。预取尊重既有的新鲜缓存不会做无谓的网络请求。staleTime决定数据是否过期。例如queryClient.query({ queryKey: [todos], queryFn: fetchTodos, staleTime: 5000, // 5 秒内视为新鲜超过则重新拉取 })当缓存数据已超过指定的staleTime时预取会重新执行queryFn未超过则直接复用缓存数据。错误处理交给useQuery预取只负责“点火”。因为useQuery会负责重试与错误展示预取阶段你只需要忽略 Promise用void忽略返回的 Promise用.catch(noop)吞掉错误即可noop是 Vue Query 导出的空操作函数见 packages/vue-query/src/queryClient.ts 中废弃 API 的用法注释。staleTime: static只要有缓存数据就永远返回。当查询被标记为static时预取不会因数据“过期”而重新请求只要缓存存在就直接返回。缓存不会永久驻留如果某个被预取的查询始终没有useQuery实例订阅例如用户最终没有进入对应页面在达到gcTime垃圾回收时间之后它会被删除并回收。为什么重试被关闭源码里的retry: false预取阶段为什么不重试从 packages/query-core/src/queryClient.ts 的实现可以看到query()在发起前会做一次兜底处理// https://github.com/tannerlinsley/react-query/issues/652 if (defaultedOptions.retry undefined) { defaultedOptions.retry false }也就是说当调用方未显式指定retry时一次性的queryClient.query调用默认不会重试。失败的重试与错误 UI 呈现是useQuery等观察者的职责预取只负责把数据放进缓存失败就静默忽略——这正是void.catch(noop)组合存在的意义。源码视角query()是如何决定“取或缓存”的query()的完整逻辑位于 packages/query-core/src/queryClient.ts核心决策只有两步用defaultQueryOptions补齐默认配置并通过this.#queryCache.build(this, defaultedOptions)构建/复用 Query 实例调用query.isStaleByTime(...)判断数据是否过期过期 →await query.fetch(defaultedOptions)重新拉取未过期 → 直接返回query.state.data缓存命中。“是否过期”的判定在 packages/query-core/src/query.ts 中实现isStaleByTime(staleTime: StaleTime 0): boolean { // no data is always stale if (this.state.data undefined) { return true } // static is never stale if (staleTime static) { return false } // if the query is invalidated, it is stale if (this.state.isInvalidated) { return true } return !timeUntilStale(this.state.dataUpdatedAt, staleTime) }从源码可以推断出三条结论没有任何数据时永远是 stale 的所以首次预取必然触发请求staleTime static直接短路返回false即“static 永不新鲜失效”这正是staleTime: static实现“只要缓存存在就直接返回”的底层原因同时参考 packages/query-core/src/query.ts 的isStatic()方法被invalidate过的查询即使没超时也会被视为 stale从而允许预取重新拉取。而在 Vue 这一侧packages/vue-query/src/queryClient.ts 对query()做了响应式封装入参类型为MaybeRefDeep内部通过cloneDeepUnref(options)深度解包 Vue 的ref/getter后再调用 query-core 的实现。因此你可以在预取参数里直接传入ref值Vue Query 会自动解包。关于废弃 API 的说明从源码注释可以确认早期版本使用的prefetchQuery、fetchQuery、ensureQueryData、prefetchInfiniteQuery、fetchInfiniteQuery在 packages/vue-query/src/queryClient.ts 与 packages/query-core/src/queryClient.ts 中均标记为deprecated官方建议统一改用queryClient.query({ ...options, staleTime: static })或queryClient.infiniteQuery(options)。其中旧的prefetchQuery行为等价于fetchQuery().then(noop).catch(noop)——也就是“取数 吞错”这与当前推荐的void query(...).catch(noop)写法语义一致。编写新代码时应直接使用新的query/infiniteQueryAPI。组合式 API 变体usePrefetchQuery 与 usePrefetchInfiniteQuery除了命令式地调用queryClient仓库还提供了响应式的组合式预取钩子适合放在路由守卫、父组件setup或watchEffect中随响应式依赖自动触发usePrefetchQuery实现在 packages/vue-query/src/usePrefetchQuery.ts核心是watchEffect(() { const resolvedOptions isGetter(options) ? options() : unref(options) const clonedOptions cloneDeepUnref(resolvedOptions) if (!client.getQueryState(clonedOptions.queryKey)) { void client.query(clonedOptions).catch(noop) } })它有两个值得注意的行为只在查询状态不存在时发起预取getQueryState返回 undefined 才执行并且支持传入ref/getterqueryKey 变化时会自动重新预取。usePrefetchInfiniteQuery实现在 packages/vue-query/src/usePrefetchInfiniteQuery.ts结构完全对称内部调用client.infiniteQuery(...)。这些行为都有对应测试用例佐证见 packages/vue-query/src/tests/usePrefetchQuery.test.ts例如“查询状态已存在时不重复预取”“queryKey 响应式变化时重新预取”“自动解包 ref 参数”等。预取 Infinite Queries无限滚动查询Infinite Query 与普通查询一样可以预取并且拥有独立的pages控制能力import { noop } from tanstack/vue-query const prefetchProjects async () { // The results of this query will be cached like a normal query await queryClient .infiniteQuery({ queryKey: [projects], queryFn: fetchProjects, initialPageParam: 0, getNextPageParam: (lastPage, pages) lastPage.nextCursor, pages: 3, // prefetch the first 3 pages }) .catch(noop) }关键行为如下默认只预取第一页。如果不传pages只有首页数据被拉取并以给定queryKey存入缓存。pages指定要预取的页数。上面的例子会按顺序预取前 3 页。每预取一页都会执行一次getNextPageParam用来推导“下一页的页码/游标”从而构造下一次请求参数。getNextPageParam返回undefined时预取立即停止。这是天然的终止条件——当接口不再返回下一页游标时预取流程结束不会出现无限请求。从实现上看infiniteQuery在 packages/query-core/src/queryClient.ts 中会先给选项打上options._type infinite标记再复用query()的统一执行管线因此“新鲜则命中、过期则拉取、static永不失效、无订阅则按gcTime回收”等所有规则对 Infinite Query 同样适用。实践建议在路由进入前预取配合 Vue Router 的beforeEnter守卫或路由组件的setup中调用usePrefetchQuery让详情页/下一页数据先于渲染就绪。合理设置staleTime高频稳定的数据可设置较长staleTime或直接用staleTime: static避免重复请求易变数据保持默认短过期策略。务必吞掉错误所有预取调用统一使用void ... .catch(noop)模式避免出现 unhandled promise rejection同时把重试与错误 UI 交给useQuery。控制 Infinite Query 的预取深度pages不是越大越好按用户滚动行为预估 13 页即可同时保证getNextPageParam正确实现终止条件。关注内存预取后若无人订阅数据会在gcTime后被自动回收无需手动清理若想长期驻留缓存保持相应useQuery实例存活或配合staleTime: static使用。预取是 Vue Query 提升体验性价比最高的手段之一——把“等待数据”变成“数据等待”配合staleTime、static与多页预取即可构建丝滑的渐进式加载体验。相关核心源码与测试可继续深入阅读 packages/vue-query/src/queryClient.ts、packages/query-core/src/queryClient.ts 与 packages/query-core/src/query.ts。【免费下载链接】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),仅供参考
返回列表