Svelte Query 完整 API 参考:@tanstack/svelte-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/svelte-query的 API 参考指南,以 docs/framework/svelte/reference/index.md 索引为核心骨架,系统梳理其全部类型别名、函数与变量,并结合仓库源码(packages/svelte-query/src)讲解createQuery、createMutation、createInfiniteQuery、createQueries等核心 API 的重载签名、响应式Accessor设计、SSR 水合与上下文机制。读完本文,你将能在 Svelte 5 项目中正确选用并组合这些 API,写出类型安全、支持响应式选项与乐观更新的数据请求代码。
API 全景:索引页揭示的完整公开面
@tanstack/svelte-query的公开 API 被组织为四类:类型别名(Type Aliases)、变量(Variables)、函数(Functions)与引用(References)。索引页完整列出的 26 个类型别名、1 个变量和 17 个函数,正是 packages/svelte-query/src/index.ts 中 re-export 的完整清单。
从索引结构可以清晰看到三条使用主线:
- 查询与数据获取:
createQuery、createInfiniteQuery、createQueries,配合queryOptions、infiniteQueryOptions共享选项; - 变更与服务端副作用:
createMutation,配合mutationOptions与useMutationState跨组件观察状态; - 上下文与生命周期:
QueryClientProvider、HydrationBoundary、useQueryClient、useHydrate、useIsFetching、useIsMutating、useIsRestoring。
值得注意的细节是,索引页的 References 一节指出QueryClientProvider是对HydrationBoundary的rename + re-export(见 variables/HydrationBoundary.md),这在 packages/svelte-query/src/index.ts#L32-L33 中通过export { default as HydrationBoundary } from './HydrationBoundary.svelte'和export { default as QueryClientProvider } from './QueryClientProvider.svelte'得到印证——Svelte Query 使用同一个 Svelte 组件承载「提供客户端」与「水合状态」两项职责。
类型别名体系:从选项到结果的类型契约
索引页列出的 26 个类型别名并非杂乱枚举,它们构成了「选项 → 结果」的完整类型映射。核心定义集中在 packages/svelte-query/src/types.ts:
基础类型:Accessor<T>定义为() => T(types.ts#L22),是 Svelte Query 响应式设计的基石——所有create*函数的第一个参数都是Accessor包裹的选项,使得选项可以随 Svelte 响应式状态变化而更新。
查询族:
| 类型别名 | 说明 |
|---|---|
CreateBaseQueryOptions/CreateBaseQueryResult | 底层选项与结果,直接透传query-core的QueryObserverOptions/QueryObserverResult |
CreateQueryOptions/CreateQueryResult | createQuery的常规选项与结果 |
DefinedCreateQueryResult | 设置了initialData时的结果,data永不为undefined |
DefinedInitialDataOptions/UndefinedInitialDataOptions | 是否携带initialData的选项类型区分 |
CreateInfiniteQueryOptions/CreateInfiniteQueryResult | 无限查询的选项与结果 |
DefinedCreateInfiniteQueryResult | 带initialData的无限查询结果 |
变更族:CreateMutationOptions、CreateMutationResult、CreateMutateFunction、CreateMutateAsyncFunction覆盖mutationFn、mutate与mutateAsync的类型;MutationTypeFromResult用于从结果反推变更类型,支撑useMutationState的泛型推导。
组合与特殊:QueriesOptions、QueriesResults支持createQueries的 tuple/数组推导;HydrationBoundary、QueryClientProviderProps定义水合组件的 props;MutationStateOptions定义useMutationState的过滤器与select参数。
从源码结构看,这些类型绝大多数是对@tanstack/query-core中QueryObserverOptions、MutationObserverOptions等类型的精确定义包装(types.ts#L25-L119),目的是让 Svelte 侧的类型与 core 保持严格一致的同时,裁剪掉_defaulted等内部字段。
createQuery:三种重载对应三种典型场景
createQuery在 packages/svelte-query/src/createQuery.ts 中定义了三个重载(Call Signature),索引页与函数文档分别对应:常规UndefinedInitialDataOptions、带initialData的DefinedInitialDataOptions、以及更宽泛的CreateQueryOptions。三个重载共享四个泛型参数:
TQueryFnData = unknown:queryFn 的原始返回类型TError = Error:错误类型,Svelte 侧默认Error(core 中为DefaultError)TData = TQueryFnData:select转换后的数据类型TQueryKey extends readonly unknown[] = readonly unknown[]:查询键类型
两个可选位置参数:options(Accessor包裹,支持响应式)与queryClient?(Accessor<QueryClient>,不传则取最近上下文的客户端)。
基础用法:status 与派生布尔值
最简单形态——用status区分三种状态,或直接用isPending/isError/isSuccess派生布尔值:
<script lang="ts"> import { createQuery } from '@tanstack/svelte-query' const query = createQuery(() => ({ queryKey: ['posts'], queryFn: fetchPosts, })) </script> {#if query.status === 'pending'} Loading... {:else if query.status === 'error'} <span>Error: {query.error.message}</span> {:else} <ul> {#each query.data as post (post.id)} <li>{post.title}</li> {/each} </ul> {/if}status的取值语义在函数文档中明确给出:无缓存数据显示时pending,最近一次抓取失败为error,有数据可展示为success;isPending/isSuccess/isError只是方便阅读的派生布尔值。
initialData 重载:data 永不为 undefined
当设置了initialData时,TypeScript 自动选择第二个重载,返回DefinedCreateQueryResult——此时status的类型层面永不解析为pending,即使重新抓取失败,旧数据也会保留:
<script lang="ts"> import { createQuery } from '@tanstack/svelte-query' // `data` 是 `Post[]`,永不 undefined——即便 refetch 失败,列表也保持可见 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>高级模式:select、依赖查询与分页
createQuery文档提供了四个实战级示例,覆盖数据获取场景的高频诉求:
1.select派生数据——缓存仍存完整的Post[],但组件拿到的data是number,且不会污染缓存:
const query = createQuery(() => ({ queryKey: ['posts'], queryFn: fetchPosts, select: (posts) => posts.length, }))2. 依赖查询——postId未就绪时禁用查询。注意文档的提醒:禁用期间要用isLoading而非isPending,否则会误显示 loading:
let { postId }: { postId: number | undefined } = $props() const query = createQuery(() => ({ queryKey: ['post', postId], queryFn: () => fetchPost(postId!), enabled: postId != null, }))3. 从缓存列表种子化详情查询——用initialData函数形式从已缓存的列表数据中取出详情,跳过加载态:
const queryClient = useQueryClient() const query = createQuery(() => ({ queryKey: ['post', postId], queryFn: () => fetchPost(postId), initialData: () => queryClient .getQueryData<Array<Post>>(['posts']) ?.find((post) => post.id === postId), }))4. 分页保持旧数据可见——placeholderData: keepPreviousData配合isPlaceholderData禁用下一页按钮:
let page = $state(0) const query = createQuery(() => ({ queryKey: ['posts', page], queryFn: () => fetchPosts(page), placeholderData: keepPreviousData, }))createInfiniteQuery:无限滚动与分页加载
createInfiniteQuery(packages/svelte-query/src/createInfiniteQuery.ts)同样有三个重载,泛型在查询族基础上增加TPageParam = unknown,TData默认值为InfiniteData<TQueryFnData, unknown>。其返回结果在CreateQueryResult基础上增加fetchNextPage/fetchPreviousPage/hasNextPage/hasPreviousPage四个分页能力。
Load More 按钮模式
通过initialPageParam指定起始页码,getNextPageParam从最后一页推导下一页参数:
<script lang="ts"> import { createInfiniteQuery } from '@tanstack/svelte-query' const query = createInfiniteQuery(() => ({ queryKey: ['projects'], queryFn: ({ pageParam }) => fetchProjects(pageParam), initialPageParam: 0, getNextPageParam: (lastPage) => lastPage.nextId, })) </script> {#if query.isPending} Loading... {:else if query.isError} <span>Error: {query.error.message}</span> {:else} <ul> {#each query.data.pages as page} {#each page.projects as project (project.id)} <li>{project.name}</li> {/each} {/each} </ul> <button onclick={() => query.fetchNextPage()} disabled={!query.hasNextPage || query.isFetching} > {query.isFetchingNextPage ? 'Loading more...' : query.hasNextPage ? 'Load More' : 'Nothing more to load'} </button> {/if}IntersectionObserver 无限滚动模式
用 Svelte 5 的$effect+bind:this挂载哨兵元素,滚动到底部自动加载:
let sentinel: HTMLDivElement | undefined = $state() $effect(() => { if (sentinel == null || !query.hasNextPage || query.isFetching) return const observer = new IntersectionObserver(([entry]) => { if (entry?.isIntersecting) query.fetchNextPage() }) observer.observe(sentinel) return () => observer.disconnect() })createMutation:服务端副作用与乐观更新
与查询不同,变更通常用于创建/更新/删除数据或执行服务端副作用。createMutation(packages/svelte-query/src/createMutation.svelte.ts)的泛型为TData = unknown、TError = Error、TVariables = void、TContext = unknown,返回CreateMutationResult。
基础:mutate 触发 + 成功后失效查询
<script lang="ts"> import { createMutation, useQueryClient } from '@tanstack/svelte-query' const queryClient = useQueryClient() const addMutation = createMutation(() => ({ mutationFn: addTodo, onSuccess: () => queryClient.invalidateQueries({ queryKey: ['todos'] }), })) </script> <button onclick={() => addMutation.mutate('Item')}>Add</button>调用点回调与批量提交
mutate/mutateAsync都接受第二个参数传入onSuccess/onError/onSettled回调,适合在调用点做导航等副作用,而不用耦合共享的变更定义。关键语义:若连续多次请求,onSuccess只会在最近一次调用后触发;mutateAsync则每次调用返回独立的 Promise,可逐个等待:
async function handleAddAll(todos: Array<string>) { try { await Promise.all(todos.map((todo) => addMutation.mutateAsync(todo))) } catch (error) { console.error('Failed to add todos:', error) } }若希望各请求独立失败也能拿到各自结果,文档建议换用Promise.allSettled,避免第一个 reject 就丢失其余结果信息。
乐观更新三件套:onMutate / onError / onSettled
文档给出了完整的乐观更新闭环:onMutate中取消进行中的查询、快照旧数据、写入新数据并返回onMutateResult;失败时onError用快照回滚;最后onSettled统一失效查询:
const addMutation = createMutation(() => ({ mutationFn: addTodo, onMutate: async (newTodo: string) => { await queryClient.cancelQueries({ queryKey: ['todos'] }) const previousTodos = queryClient.getQueryData<Array<string>>(['todos']) queryClient.setQueryData<Array<string>>(['todos'], (old) => [ ...(old ?? []), newTodo, ]) // 失败时传给 onError 作为 onMutateResult return { previousTodos } }, onError: (_err, _newTodo, onMutateResult) => { queryClient.setQueryData(['todos'], onMutateResult?.previousTodos) }, onSettled: () => { queryClient.invalidateQueries({ queryKey: ['todos'] }) }, }))createQueries:并行查询与结果合并
createQueries(packages/svelte-query/src/createQueries.svelte.ts)接收一个对象:queries数组(每个元素是一个查询选项,支持 tuple 类型推导)+ 可选combine函数,整体由Accessor包裹实现响应式。返回按queries顺序排列的结果数组;提供combine时返回combine的产物。
let { ids }: { ids: Array<number> } = $props() const postQueries = createQueries(() => ({ queries: ids.map((id) => ({ queryKey: ['post', id], queryFn: () => fetchPost(id), staleTime: Infinity, })), }))配合combine把多个查询聚合成单一对象,方便统一渲染:
const combined = createQueries(() => ({ queries: ids.map((id) => ({ queryKey: ['post', id], queryFn: () => fetchPost(id), })), combine: (postQueries) => ({ data: postQueries.map((query) => query.data), isPending: postQueries.some((query) => query.isPending), isError: postQueries.some((query) => query.isError), }), }))选项构造器:queryOptions / infiniteQueryOptions / mutationOptions
这三者在 index.md 中并列,用于把选项定义与组件解耦,既可在create*中共享,也能被queryClient.query、queryClient.infiniteQuery等命令式 API 复用。
queryOptions(packages/svelte-query/src/queryOptions.ts):queryKey必填,返回值使queryKey携带推断出的数据类型(QueryKeyWithDataTag)。有initialData与无initialData两个重载。典型用法是参数化工厂,按id复用同一套选项:
const postOptions = (id: string) => queryOptions({ queryKey: ['post', id], queryFn: () => fetchPost(id), }) const query = createQuery(() => postOptions(id))infiniteQueryOptions(packages/svelte-query/src/infiniteQueryOptions.ts):同样queryKey必填,TData默认InfiniteData<TQueryFnData, unknown>。可用initialData: { pages: [], pageParams: [] }跳过首屏加载态:
const projectsOptions = infiniteQueryOptions({ queryKey: ['projects'], queryFn: ({ pageParam }) => fetchProjects(pageParam), initialPageParam: 0, getNextPageParam: (lastPage) => lastPage.nextId, initialData: { pages: [], pageParams: [] }, })mutationOptions(packages/svelte-query/src/mutationOptions.ts):两个重载的关键差异在于mutationKey——带mutationKey的重载返回WithRequired<..., "mutationKey">,可用于useMutationState跨组件查询变更状态;不带的重载返回Omit<..., "mutationKey">:
const createPostOptions = mutationOptions({ mutationKey: ['posts', 'create'], mutationFn: createPost, }) const pending = useMutationState({ filters: { mutationKey: createPostOptions.mutationKey, status: 'pending' }, })上下文机制:QueryClient 的存取与恢复状态
Svelte Query 依赖 Svelte 的 context API 传递QueryClient。核心实现在 packages/svelte-query/src/context.ts:
setQueryClientContext(client)通过setContext(Symbol('QueryClient'), client)写入;getQueryClientContext()通过getContext读取,找不到时会抛出"No QueryClient was found in Svelte context. Did you forget to wrap your component with QueryClientProvider?"(context.ts#L14-L23),这解释了为什么所有create*函数都必须处于QueryClientProvider之下;setIsRestoringContext/getIsRestoringContext以Symbol('isRestoring')为键存取恢复状态布尔值,getIsRestoringContext在取不到时安全回退为{ current: false },不会抛错。
useQueryClient(packages/svelte-query/src/useQueryClient.ts)是对getQueryClientContext的薄封装,可传可选参数指定自定义客户端,否则取最近上下文。
useIsRestoring用于在持久化恢复过程中抑制抓取,配合 packages/svelte-query/src/useIsRestoring.ts 读取 context 中的恢复标记。
全局状态观察:useIsFetching / useIsMutating / useMutationState
这类函数让「不在同一组件内的请求/变更」也能被全局观察到:
useIsFetching(filters?, queryClient?)(packages/svelte-query/src/useIsFetching.svelte.ts)返回ReactiveValue<number>——注意返回值是响应式容器,需读.current取当前正在抓取的查询数量。filters可传QueryFilters收窄范围(如{ queryKey: ['posts'] }),省略则统计全部:
const isFetchingPosts = useIsFetching({ queryKey: ['posts'] }) {#if isFetchingPosts.current} <span>Refreshing posts...</span> {/if}useIsMutating与前者对称,统计匹配过滤条件的进行中变更数量,常用于全局「保存中…」指示。useMutationState(options?, queryClient?)(packages/svelte-query/src/useMutationState.svelte.ts)可访问任何匹配filters的变更——包括由其他组件或 hook 创建、甚至已卸载的。options为MutationStateOptions(默认{}),含filters与可选的select。返回值是对每个匹配变更应用select后的数组:
const pendingVariables = useMutationState({ filters: { status: 'pending' }, select: (mutation) => mutation.state.variables, }) {pendingVariables.length} posts saving...文档还提示一个重要细节:每次mutate调用都会在变更缓存中保留一个条目直到gcTime过期,因此取useMutationState返回数组的最后一个元素即最近一次成功的变更(配合status: 'success'过滤):
const savedPosts = useMutationState({ filters: { mutationKey: ['posts'], status: 'success' }, select: (mutation) => mutation.state.data, }) const latestSavedPost = $derived(savedPosts[savedPosts.length - 1])SSR 与状态水合:useHydrate / HydrationBoundary
useHydrate(state?, options?, queryClient?)(packages/svelte-query/src/useHydrate.ts)把之前dehydrate出的状态并入queryClient;若客户端已有数据,会基于更新时间戳智能合并。HydrationBoundary是对它的封装,日常 SSR 场景直接使用组件形式即可——除非需要在自定义组件中手动触发水合,才直接用useHydrate。
典型的 SSR 流程:服务端 load 函数用queryClient.query预取并dehydrate(queryClient),客户端通过 props 接收dehydratedState交给HydrationBoundary:
<script lang="ts"> import { HydrationBoundary } from '@tanstack/svelte-query' import type { DehydratedState } from '@tanstack/svelte-query' import Posts from './Posts.svelte' let { dehydratedState }: { dehydratedState: DehydratedState } = $props() </script> <HydrationBoundary state={dehydratedState}> <Posts /> </HydrationBoundary>扩展阅读与源码索引
围绕本文涉及的 API,可在仓库中继续深入:
- 全部类型别名与函数的逐条签名:docs/framework/svelte/reference/type-aliases 与 docs/framework/svelte/reference/functions
- Svelte Query 实现源码:packages/svelte-query/src/index.ts、createQuery.ts、createMutation.svelte.ts、context.ts、types.ts
- 框架总览与安装方式:docs/framework/svelte/overview.md、docs/framework/svelte/installation.md
- Svelte Query 官方示例(basic、load-more-infinite-scroll、optimistic-updates、ssr 等):examples/svelte
- Svelte 5 迁移说明:docs/framework/svelte/migrate-from-v5-to-v6.md
理解这些 API 时把握三条主线即可:查询用createQuery/createInfiniteQuery/createQueries加选项构造器共享定义;变更用createMutation配合useMutationState实现跨组件观察与乐观更新;上下文由QueryClientProvider提供、useQueryClient消费,SSR 场景经dehydrate/HydrationBoundary完成状态交接。
【免费下载链接】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),仅供参考