Svelte Query CreateQueryOptions 类型指南:createQuery 全部选项的 TypeScript 权威解读
【免费下载链接】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
CreateQueryOptions是 TanStack Svelte Query 中createQuery组合式函数(hook)的参数类型,它描述了如何声明一次数据请求的全部行为:从queryKey、queryFn这类必填核心字段,到enabled、staleTime、retry、select等数十个用于控制缓存、重试、自动刷新与派生数据的可选配置。本文以该类型的源码定义为主体,完整解析其类型参数、继承链上的全部字段及其默认值与底层实现,帮助你写出类型安全、行为可控的 Svelte 数据获取代码。
一、类型定义:一行别名背后的继承链
CreateQueryOptions定义在 packages/svelte-query/src/types.ts 中,其完整声明如下:
/** Options for createQuery */ export type CreateQueryOptions< TQueryFnData = unknown, TError = DefaultError, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, > = CreateBaseQueryOptions<TQueryFnData, TError, TData, TQueryFnData, TQueryKey>从源码结构看,这是一个层层转发的类型别名:
CreateQueryOptions→CreateBaseQueryOptions(定义于 packages/svelte-query/src/types.ts 第 24-31 行)CreateBaseQueryOptions→QueryObserverOptions(来自@tanstack/query-core)
即CreateQueryOptions最终等价于QueryObserverOptions<TQueryFnData, TError, TData, TQueryFnData, TQueryKey>。注意其中TQueryData(观测器内部缓存的原始数据类型)被固定为TQueryFnData,这保证了select变换前的数据形态与查询函数返回的数据形态一致。
QueryObserverOptions定义于 packages/query-core/src/types.ts,它通过WithRequired<QueryOptions<...>, 'queryKey'>强制要求queryKey必填,并在此之上补充了观测器(observer)层面的行为选项。因此,CreateQueryOptions的字段空间 =QueryOptions的全部字段 +QueryObserverOptions的全部字段。
二、四个类型参数:从数据形态到错误类型的完整约束
原文档声明了 4 个泛型参数,理解它们之间的默认值联动关系是掌握该类型的关键:
| 类型参数 | 约束 | 默认值 | 含义 |
|---|---|---|---|
TQueryFnData | 无 | unknown | queryFn返回的原始数据类型,即缓存中存储的数据类型 |
TError | 无 | DefaultError | 查询失败时error的类型,默认是unknown的包装 |
TData | 无 | TQueryFnData | 组件实际读取到的数据类型;设置select后TData为选择器返回值 |
TQueryKey | extends QueryKey | QueryKey | 查询键类型,必须是QueryKey(readonly unknown[])的子类型 |
关键联动逻辑:
- 若不传
TData,它默认等于TQueryFnData,因此普通查询的query.data类型就是queryFn的返回类型; - 一旦使用
select: (data) => ...,TData会被推断为选择器的返回类型(见下文实战示例); TQueryKey默认是宽泛的QueryKey,但显式传入字面量类型(如['post', postId])可获得更精确的查询键类型推导,配合queryOptions还能让queryKey携带数据类型标签(QueryKeyWithDataTag)。
这四个参数贯穿整个 Svelte Query 的类型体系:CreateQueryResult<TData, TError>、DefinedCreateQueryResult<TData, TError>等结果类型都与之对应,保证"选项类型 → 结果类型"的完全一致。
三、完整选项字段清单:全部配置项、默认值与作用
以下字段是CreateQueryOptions可接受的全部配置,分为两层列出(来源:packages/query-core/src/types.ts)。
3.1 QueryObserverOptions 层:控制组件观测行为
| 字段 | 类型 | 默认值 | 作用 |
|---|---|---|---|
enabled | boolean \| (query) => boolean | true | 设为false时挂载或查询键变化不会自动请求,需手动调用refetch |
staleTime | number \| (query) => number | 0 | 数据被视为过期的时间(毫秒);Infinity表示永不过期 |
refetchInterval | number \| false \| (query) => number \| false \| undefined | false | 定时轮询频率(毫秒),函数形式可根据最新数据动态计算 |
refetchIntervalInBackground | boolean | false | true时标签页/窗口在后台也继续轮询 |
refetchOnWindowFocus | boolean \| 'always' \| (query) => ... | true | 窗口聚焦且数据过期时自动重新请求;'always'无条件刷新 |
refetchOnReconnect | boolean \| 'always' \| (query) => ... | true(networkMode: 'always'时为false) | 网络重连时自动重新请求 |
refetchOnMount | boolean \| 'always' \| (query) => ... | true | 组件挂载时若数据过期则刷新;false阻止同一查询的额外实例触发后台刷新 |
retryOnMount | boolean \| (query) => boolean | true | 挂载时若查询曾失败是否再次重试 |
notifyOnChangeProps | string[] \| 'all' \| (() => string[]) | 跟踪访问属性 | 仅当列出的属性变化时触发组件重渲染;默认按访问追踪 |
throwOnError | boolean \| (error, query) => boolean | false | true或配合suspense时把错误抛给错误边界,而不是放入error状态 |
select | (data: TQueryData) => TData | 无 | 从缓存数据变换出组件需要的部分数据,不改变缓存内容 |
suspense | boolean | false | true时status === 'pending'挂起、status === 'error'抛错 |
placeholderData | TQueryData \| 函数 | 无 | 无initialData且数据加载中时显示的占位数据(如keepPreviousData) |
_optimisticResults | 'optimistic' \| 'isRestoring' | 无 | 内部使用的乐观结果标记 |
3.2 QueryOptions 层:查询本身的运行与缓存策略
| 字段 | 类型 | 默认值 | 作用 |
|---|---|---|---|
queryKey | TQueryKey | 必填(无默认) | 查询的唯一标识,用于缓存命中与失效 |
queryFn | QueryFunction \| SkipToken | 无 | 实际发起请求的函数;使用skipToken可跳过请求 |
retry | boolean \| number \| (failureCount, error) => boolean | 3 | 失败重试次数;true无限重试,false不重试 |
retryDelay | number \| (retryAttempt, error) => number | 指数退避 | 重试间隔(毫秒),默认按重试次数指数递增 |
networkMode | 'online' \| 'always' \| 'offlineFirst' | 'online' | 控制网络不可用时的行为 |
gcTime | number | 5 分钟(默认) | 缓存变为未使用/非活动后保留在内存的时间(毫秒),Infinity关闭垃圾回收 |
queryHash | string | 由键哈希生成 | 查询哈希,用于内部定位查询 |
queryKeyHashFn | (queryKey) => string | 默认哈希 | 自定义查询键哈希函数 |
initialData | TData \| () => TData | 无 | 首次渲染即有的初始数据,可避免 loading 状态 |
initialDataUpdatedAt | number \| () => number \| undefined | 无 | initialData的时间戳,影响 stale 判断 |
structuralSharing | boolean \| (oldData, newData) => unknown | true | 结构共享,数据形状未变时复用旧引用避免多余重渲染 |
persister | QueryPersister | 无 | 自定义查询持久化器 |
behavior | QueryBehavior | 无 | 查询行为扩展 |
meta | QueryMeta | 无 | 附加到查询上的任意负载,供其他地方读取 |
maxPages | number | 无 | 无限查询最大缓存页数 |
注意:以上默认值(如
retry: 3、gcTime: 5 分钟)为 Query 核心的常规默认,实际生效值还取决于QueryClient构造时的全局默认配置,二者会合并。
四、响应式 Accessor 包裹:Svelte 5 特有的选项声明方式
createQuery的选项参数被包装为Accessor<T>(即() => T),这是 Svelte Query 适配 Svelte 5 runes 响应式系统的核心设计。createQuery的最终实现(见 packages/svelte-query/src/createQuery.ts):
export function createQuery( options: Accessor<CreateQueryOptions>, queryClient?: Accessor<QueryClient>, ) { return createBaseQuery(options, QueryObserver, queryClient) }这意味着在.svelte组件中,你需要把选项包在一个函数里,任何$state/$props的变化都会让选项重新求值:
<script lang="ts"> import { createQuery } from '@tanstack/svelte-query' let { postId }: { postId: number | undefined } = $props() const query = createQuery(() => ({ queryKey: ['post', postId], queryFn: () => fetchPost(postId!), enabled: postId != null, })) </script>queryClient同样是可选的Accessor<QueryClient>,不传时使用最近上下文中的客户端。
五、initialData 重载:类型上消灭undefined
createQuery依据是否提供initialData选择了不同重载(packages/svelte-query/src/queryOptions.ts):
- 未提供
initialData时使用UndefinedInitialDataOptions,此时TQueryFnData被NonUndefinedGuard约束; - 提供
initialData时使用DefinedInitialDataOptions,返回DefinedCreateQueryResult,其中data保证永不为undefined,status不会解析为pending(除非请求失败且保留旧数据)。
<script lang="ts"> import { createQuery } from '@tanstack/svelte-query' // data 是 Post[],绝不会是 undefined const query = createQuery(() => ({ queryKey: ['posts'], queryFn: fetchPosts, initialData: [], })) </script> {#if query.isError} <span>Error: {query.error.message}</span> {/if} <ul> {#each query.data as post (post.id)} <li>{post.title}</li> {/each} </ul>六、queryOptions:把选项提升为可共享、可复用的一等公民
queryOptions接受与createQuery完全相同的选项对象,返回时给queryKey附加数据类型标签(QueryKeyWithDataTag),使选项既可用于组件内的createQuery,也可用于命令式 API(如queryClient.query、queryClient.fetchQuery),实现一份定义多处消费:
<script lang="ts"> import { queryOptions, createQuery } from '@tanstack/svelte-query' const postOptions = (id: string) => queryOptions({ queryKey: ['post', id], queryFn: () => fetchPost(id), }) let { id }: { id: string } = $props() const query = createQuery(() => postOptions(id)) </script>七、常用配置组合实战
结合 createQuery.ts 的文档示例 与上文字段,以下是几组高频组合。
7.1 select 派生数据而不污染缓存
<script lang="ts"> import { createQuery } from '@tanstack/svelte-query' // 缓存仍存完整 Post[],组件只读取数量 const query = createQuery(() => ({ queryKey: ['posts'], queryFn: fetchPosts, select: (posts) => posts.length, })) </script> {#if query.isPending} Loading... {:else if query.isError} <span>Error: {query.error.message}</span> {:else} <span>{query.data} posts</span> {/if}7.2 initialData 从缓存播种详情页
<script lang="ts"> import { createQuery, useQueryClient } from '@tanstack/svelte-query' let { postId }: { postId: number } = $props() const queryClient = useQueryClient() const query = createQuery(() => ({ queryKey: ['post', postId], queryFn: () => fetchPost(postId), initialData: () => queryClient .getQueryData<Array<Post>>(['posts']) ?.find((post) => post.id === postId), })) </script>7.3 分页时保留上一页数据
<script lang="ts"> import { createQuery, keepPreviousData } from '@tanstack/svelte-query' let page = $state(0) const query = createQuery(() => ({ queryKey: ['posts', page], queryFn: () => fetchPosts(page), placeholderData: keepPreviousData, })) </script> <button disabled={query.isPlaceholderData} onclick={() => page++}> Next Page </button>7.4 依赖查询:enabled 与 isLoading 配合
依赖其他数据的查询应使用enabled关闭自动请求,并用isLoading(而非isPending)判断,避免禁用期间误显示加载态:
<script lang="ts"> import { createQuery } from '@tanstack/svelte-query' let { postId }: { postId: number | undefined } = $props() const query = createQuery(() => ({ queryKey: ['post', postId], queryFn: () => fetchPost(postId!), enabled: postId != null, })) </script> {#if postId == null} Select a post {:else if query.isLoading} Loading... {:else if query.isError} <span>Error: {query.error.message}</span> {:else} <h1>{query.data?.title}</h1> {/if}八、关联类型一览
CreateQueryOptions不是孤立存在,它与 Svelte Query 类型体系中的以下成员紧密关联(均定义于 packages/svelte-query/src/types.ts):
CreateBaseQueryOptions:底层选项类型,比CreateQueryOptions多一个TQueryData参数,供内部createBaseQuery使用;CreateQueryResult<TData, TError>:createQuery的返回值类型;DefinedCreateQueryResult:提供initialData时保证data非空的返回类型;CreateInfiniteQueryOptions:createInfiniteQuery的选项类型,额外包含initialPageParam、getNextPageParam、getPreviousPageParam与maxPages;UndefinedInitialDataOptions/DefinedInitialDataOptions:createQuery两个重载使用的细分选项类型。
理解CreateQueryOptions就等于掌握了 Svelte Query 声明式数据获取的全部旋钮:从数据形态(四个泛型参数)、请求时机(enabled/refetchOn*)、失败策略(retry/retryDelay/throwOnError)、缓存寿命(gcTime/staleTime/structuralSharing)到派生展示(select/placeholderData/initialData),均可在类型系统的保护下组合使用。
【免费下载链接】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),仅供参考