ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

TanStack Query 之 Initial Query Data:为 Vue Query 查询预填充缓存的完整指南

TanStack Query 之 Initial Query Data:为 Vue Query 查询预填充缓存的完整指南 TanStack Query 之 Initial Query Data为 Vue 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在 TanStack QueryVue Query中查询缓存是数据流的中心。很多时候你的应用在发起网络请求之前就已经持有部分数据例如路由状态、状态管理库中的快照、上一屏查询的缓存结果如果能让这些数据直接进入查询缓存并跳过 loading 状态体验会好很多。本篇指南以docs/framework/vue/guides/initial-query-data.md为核心系统讲解如何通过initialData以声明式方式为查询预填充缓存深入剖析它与staleTime、initialDataUpdatedAt的配合逻辑并结合仓库源码query.ts、useBaseQuery.ts揭示底层实现原理。读完本文你将掌握从零缓存秒开与从旧缓存优雅过渡两种实战方案并能正确区分initialData与placeholderData的适用场景。一、为什么需要 Initial Query Data三种预填充缓存的途径在真正需要某条查询数据之前你有多种方式把数据喂给缓存。TanStack Query 官方指南将其分为两大类声明式Declaratively在查询选项中提供initialData如果缓存为空就用这份数据预填充缓存命令式Imperatively使用queryClient.prefetchQuery或usePrefetchQuery预先拉取数据并写入缓存详见 prefetching 指南使用queryClient.setQueryData手动把数据直接放进缓存同样详见 prefetching 指南。两者的本质区别在于命令式方式在缓存被填充时就已经完成了一次取数而声明式方式只是把一份你手上已有的数据作为查询的初始状态。下文重点展开声明式方案。二、使用initialData预填充查询当你的应用在发起请求之前就已经拥有某条查询的初始数据时可以直接把它传给查询选项。这种情况下initialData会让查询跳过初始的 loading 状态首次渲染即可展示数据import { useQuery } from tanstack/vue-query // 假设 initialTodos 已在应用中可用 const result useQuery({ queryKey: [todos], queryFn: () fetch(/todos).then((res) res.json()), initialData: initialTodos, })重要提示initialData会被持久化到缓存persisted to the cache因此不建议向它传入占位性的、部分的或不完整的数据这类场景应当使用placeholderData详见 placeholder-query-data 指南。从源码层面看这一行为由 query.ts 中的getDefaultState函数落实当查询实例初始化时它会读取options.initialData并写入查询状态只要data ! undefined查询的初始status就是success而非pendingdataUpdatedAt也会被设置。这就是跳过 loading 状态这一行为的底层依据。2.1staleTime与initialDataUpdatedAt的交互默认情况下initialData会被当作完全新鲜的数据仿佛刚刚从查询函数取回。这意味着它会直接影响staleTime对数据的判断。三种配置对应三种行为场景一只传initialData不配置staleTime默认staleTime为0查询挂载后会立即重新请求// 会立即展示 initialTodos但挂载后也会立刻重新请求 todos const result useQuery({ queryKey: [todos], queryFn: () fetch(/todos).then((res) res.json()), initialData: initialTodos, })场景二initialDatastaleTime: 1000数据在1000毫秒内被视为新鲜与刚从查询函数取回的效果一致// 立即展示 initialTodos但 1000 毫秒内遇到其他交互事件也不会触发重新请求 const result useQuery({ queryKey: [todos], queryFn: () fetch(/todos).then((res) res.json()), initialData: initialTodos, staleTime: 1000, })场景三initialDataUpdatedAt——当你的初始数据没那么新鲜前两种配置都隐含了一个假设initialData是刚刚生成的。但实际场景中这份数据可能是 10 秒前甚至 10 分钟前缓存在应用里的。此时应使用initialDataUpdatedAt传入这份数据最近一次更新的 JavaScript 时间戳毫秒即Date.now()返回的那类数值// 立即展示 initialTodos若数据距今超过 1 分钟挂载时就会重新请求 const result useQuery({ queryKey: [todos], queryFn: () fetch(/todos).then((res) res.json()), initialData: initialTodos, staleTime: 60 * 1000, // 1 minute // 这份数据可能是 10 秒前更新的也可能是 10 分钟前更新的 initialDataUpdatedAt: initialTodosUpdatedTimestamp, // 例如 1608412420052 })注意如果拿到的 Unix 时间戳秒需要乘以1000转换成 JavaScript 毫秒时间戳。initialDataUpdatedAt让staleTime回归其本来用途——数据需要多新鲜——同时让查询在挂载时自行判断若initialData的年龄已超过staleTime就触发重新请求否则直接使用。底层逻辑在 query.ts 中一目了然dataUpdatedAt优先取initialDataUpdatedAt支持函数形式未提供时才回退到Date.now()若没有数据则为0。如果你更愿意把这份数据当作预取prefetched数据来处理官方建议改用prefetchQuery等命令式 API 预先填充缓存从而让staleTime的配置与initialData解耦详见 prefetching 指南。2.2 Initial Data 函数延迟到查询初始化时执行如果获取初始数据的过程代价较高例如需要遍历大量内存数据或你不想让它在每次渲染时都执行可以把函数传给initialData。该函数只在查询初始化时执行一次从而节省内存与 CPUconst result useQuery({ queryKey: [todos], queryFn: () fetch(/todos).then((res) res.json()), initialData: () getExpensiveTodos(), })这与getDefaultState的实现完全对应query.ts 中会先判断typeof options.initialData function是函数则立即调用一次取结果。类型层面InitialDataFunctionT () T | undefined定义于 types.ts且initialData与initialDataUpdatedAt均声明为值或函数两种形态见 types.ts。需要强调的是这里的只执行一次指的是查询实例初始化这一时刻而不是每次组件渲染。这与queryFn每次请求都会执行不同是initialData函数的核心价值。三、从其他查询的缓存中派生初始数据3.1 基础用法用列表缓存作为详情页初始数据一个非常典型的场景你有一个 todos 列表查询当用户点击某一条进入详情页时完全可以用列表缓存里的对应项作为详情查询的初始数据让详情页瞬间呈现而不是白屏等待import { useQuery, useQueryClient } from tanstack/vue-query const queryClient useQueryClient() const result useQuery({ queryKey: [todo, todoId], queryFn: () fetch(/todos/${todoId}).then((res) res.json()), initialData: () { // 从 todos 查询的缓存中取出一条 todo作为本条查询的初始数据 return queryClient.getQueryData([todos])?.find((d) d.id todoId) }, })3.2 进阶配合initialDataUpdatedAt继承源查询的新鲜度从缓存取初始数据意味着源查询上面的[todos]很可能已经过时。与其用一个人为设定的staleTime来强行阻止立即重新请求官方建议把源查询的dataUpdatedAt透传给initialDataUpdatedAt。这样查询实例就能基于真实数据年龄自行判断是否需要重新请求const result useQuery({ queryKey: [todo, todoId], queryFn: () fetch(/todos/${todoId}).then((res) res.json()), initialData: () queryClient.getQueryData([todos])?.find((d) d.id todoId), initialDataUpdatedAt: () queryClient.getQueryState([todos])?.dataUpdatedAt, })这里initialDataUpdatedAt也使用了函数形式每次求值时读取源查询的当前dataUpdatedAt保证时间戳始终与缓存真实状态同步。类型定义中initialDataUpdatedAt?: number | (() number | undefined)明确支持这种写法见 types.ts。3.3 条件式初始数据源缓存太旧就回退到加载态如果源查询太旧你甚至根本不想使用这份缓存数据直接回退到硬加载状态。此时用queryClient.getQueryState读取源查询的更多信息包括state.dataUpdatedAt时间戳自行判断新鲜度即可const result useQuery({ queryKey: [todo, todoId], queryFn: () fetch(/todos/${todoId}).then((res) res.json()), initialData: () { // 获取源查询的状态 const state queryClient.getQueryState([todos]) // 如果查询存在且其数据距今不超过 10 秒…… if (state Date.now() - state.dataUpdatedAt 10 * 1000) { // 返回对应的单个 todo return state.data.find((d) d.id todoId) } // 否则返回 undefined让查询进入硬加载状态status 为 pending }, })这段代码的本质是initialData函数返回undefined时getDefaultState中hasData为false查询状态直接回到status: pending见 query.ts也就是正常的加载流程。四、Vue 场景下的响应式细节本指南文档面向 Vue 框架useQuery的用法与 React 版保持一致的选项模型initialData、initialDataUpdatedAt、staleTime等选项均直接继承自QueryObserverOptions见 useQuery.ts。但在 Vue 组合式 API 中有几个值得注意的点必须在setup()或作用域内调用useBaseQuery在开发模式下会检查getCurrentScope()若在setup()或 effect scope 之外调用会发出内存泄漏警告见 useBaseQuery.ts。选项可以是响应式的UseQueryOptions支持MaybeRefOrGetter因此initialData、queryKey等都可以传入 ref 或 getteruseBaseQuery内部通过cloneDeepUnref解包响应式值后再传给client.defaultQueryOptions见 useBaseQuery.ts。这意味着基于todoIdref 动态取缓存的写法天然生效。返回值为响应式 refuseQuery返回的结果如data、isLoading、isSuccess均以 ref 形式暴露在模板中会被自动解包。五、进一步阅读想深入理解initialData与placeholderData的差异与取舍参见 placeholder-query-data 指南想掌握命令式预取缓存的完整姿势参见 prefetching 指南想了解查询的新鲜度与失效机制参见 query-invalidation 指南 与 important-defaults 指南。六、总结initialData是 TanStack QueryVue Query中数据已在手场景的最优解它声明式地将已有数据写入缓存、跳过 loading 状态且与staleTime、initialDataUpdatedAt组合后能精确控制何时重新请求。其底层实现集中在 query.ts 的getDefaultState初始状态构建与 types.ts 的选项类型定义中Vue 侧的响应式封装则在 useBaseQuery.ts 中完成。掌握本文的三种initialDataUpdatedAt用法直接时间戳、源查询dataUpdatedAt透传、条件式新鲜度判断即可在真实项目中写出既秒开又不过时的新缓存策略。【免费下载链接】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),仅供参考
返回列表