Solid Query 查询函数(Query Functions)实战指南:Promise 契约、错误处理与 QueryFunctionContext

发布时间:2026/9/9 12:35:20
Solid Query 查询函数(Query Functions)实战指南:Promise 契约、错误处理与 QueryFunctionContext Solid Query 查询函数Query Functions实战指南Promise 契约、错误处理与 QueryFunctionContext【免费下载链接】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 Query 在 Solid 生态的官方实现tanstack/solid-query本仓库 packages/solid-query中的Query Functions概念展开。查询函数是useQuery获取数据的核心引擎理解函数必须返回 Promise、要么 resolve 数据要么抛错这一契约并掌握QueryFunctionContext的用法是正确写出可缓存、可取消、可重试查询的基础。读完本文你将能写出规范的查询函数、优雅地处理各类请求错误并利用queryKey、signal等上下文信息实现可复用的查询逻辑。Solid 与 React 版本的此篇指南共享同一份原始素材见 docs/framework/react/guides/query-functions.md但本文将完全围绕 Solid Query 的函数式选项语法options 以函数形式传入与 SolidJS 响应式特性展开。一、查询函数的基本规则一个Promise 契约在 Solid Query 中query function查询函数可以是任何返回 Promise 的函数其返回值遵循两个硬性约定成功时 resolve 数据——Promise 成功解析出来的值会写入查询缓存并成为data失败时抛错或 reject——函数抛出的任何异常都会被捕获持久化到该查询的error状态上。此外成功解析的值不能是undefined。解析为undefined的查询会被当作失败处理历史上undefined是成功查询的非法缓存值。如果你确实想缓存空这一成功结果请显式 resolvenull。从类型定义上看这一契约被直接编码在核心层packages/query-core/src/types.ts#L102-L106export type QueryFunction T unknown, TQueryKey extends QueryKey QueryKey, TPageParam never, (context: QueryFunctionContextTQueryKey, TPageParam) T | PromiseT也就是说查询函数既可以同步返回数据T也可以异步返回 PromisePromiseT最终都会由底层以 Promise 语义统一处理。二、Solid Query 的独特语法options 以 Accessor 函数传入在使用示例前必须先强调 Solid Query 与 React Query 的一个关键差异useQuery接收的不是一个普通对象而是一个返回 options 对象的函数Accessor。这与 React 版useQuery({ queryKey, queryFn })的写法不同。看 useQuery.ts 的实现即可理解原因options 被包装进createMemo(() options())因此其中引用到的信号signal即 SolidJS 响应式状态发生变化时查询选项会自动重建并触发重取refetch。这是 SolidJS 细粒度响应式在查询层面的体现。import { useQuery } from tanstack/solid-query const todoId 1 useQuery(() ({ queryKey: [todos], queryFn: fetchAllTodos })) useQuery(() ({ queryKey: [todos, todoId], queryFn: () fetchTodoById(todoId), })) useQuery(() ({ queryKey: [todos, todoId], queryFn: async () { const data await fetchTodoById(todoId) return data }, })) useQuery(() ({ queryKey: [todos, todoId], queryFn: ({ queryKey }) fetchTodoById(queryKey[1]), }))以上四种写法都是合法且等价的配置直接引用函数、闭包捕获参数、async/await包装、以及从queryKey上下文解构参数。在真实项目中参考 examples/solid/basic/src/index.tsx#L26-L34典型用法是把queryKey作为依赖跟踪信号让todoId变化时自动重新拉取import { useQuery } from tanstack/solid-query import { createSignal } from solid-js function createPost(postId: () number) { return useQuery(() ({ queryKey: [post, postId()], queryFn: () getPostById(postId()), enabled: !!postId(), })) }注意若queryFn中通过闭包引用了外部参数而queryKey没有同步纳入这些参数底层将无法正确识别查询变化。关于 key 的设计原则可参考 Query Keys 指南关于响应式入参的更多细节可结合 Queries 基础指南 阅读。三、错误抛出与处理throw 与 rejected Promise要让 Solid Query 判断一个查询出错查询函数必须主动抛出异常throw或返回一个 rejected 的 Promise。任何在查询函数内部被抛出的错误都会进入查询的error状态并可通过解构error读取const todosQuery useQuery(() ({ queryKey: [todos, todoId], queryFn: async () { if (somethingGoesWrong) { throw new Error(Oh no!) } if (somethingElseGoesWrong) { return Promise.reject(new Error(Oh no!)) } return data }, }))在 UI 层tanstack/solid-query返回的查询结果对象是响应式的 store你可以直接基于status/isError/isPending等字段做渲染分支见 examples/solid/basic/src/index.tsx#L43-L77 中的Switch/Match用法const state createPosts() // ... Switch Match when{state.status pending}Loading.../Match Match when{state.status error} spanError: {(state.error as Error).message}/span /Match Match when{state.data ! undefined} For each{state.data}{(post) p{post.title}/p}/For /Match /Switch值得留意的是错误必须由查询函数主动抛出。Solid Query 无法替你把非 2xx 的 HTTP 响应翻译成异常——这取决于你使用的请求工具本身这正是下一节要解决的问题。四、配合fetch使用处理默认不抛错的客户端axios、graphql-request等工具库通常会对不成功的 HTTP 调用自动抛错但原生fetch默认不会对 4xx / 5xx 响应抛错它只会在网络层面失败如断网、DNS 解析失败时 reject。因此当你使用fetch时必须自己手动抛出错误。一种常见的写法是检查response.okuseQuery(() ({ queryKey: [todos, todoId], queryFn: async () { const response await fetch(/todos/ todoId) if (!response.ok) { throw new Error(Network response was not ok) } return response.json() }, }))这条规则很容易被忽略如果漏掉response.ok检查接口返回 404 时查询仍会被当作成功数据层将拿到空内容而 UI 层毫无感知。把HTTP 状态码错误显式转换为抛错才能让 Solid Query 的retry、error、错误边界等机制完整生效。请求重试策略详见 Query Retries 指南若需引用可参见 solid 同名目录此处不再展开。五、Query Function VariablesQueryKey 如何顺路进入查询函数查询键queryKey不仅用于唯一标识所获取的数据还会作为QueryFunctionContext的一部分被自动传入查询函数。这意味着你可以把查询函数抽离成独立模块而不必依赖闭包捕获外部变量function Todos(props) { const todosQuery useQuery(() ({ queryKey: [todos, { status: props.status, page: props.page }], queryFn: fetchTodoList, })) } // 直接在查询函数里解构 key、status 和 page function fetchTodoList({ queryKey }) { const [_key, { status, page }] queryKey return new Promise() }这一模式的工程意义在于查询函数与组件解耦组件侧只需声明queryKey把它当作 UI 参数的投影查询函数侧通过结构一致的queryKey数组还原出参数天然可单独测试、可复用由于queryKey同时参与缓存寻址这样写还能保证参数与缓存键永不脱节。配合 default-query-function.md 中介绍的默认查询函数可以做到完全不传queryFn上述解耦价值会被进一步放大。六、QueryFunctionContext 详解QueryFunctionContext是传给每个查询函数的唯一参数对象也是查询函数变量机制的底层来源。其构成字段如下字段类型说明queryKeyQueryKey本次查询的键即定义查询时传入的queryKey详见 Query Keys 指南clientQueryClient当前使用的QueryClient实例API 参考见 QueryClientsignalAbortSignal由 TanStack Query 提供的AbortSignal实例可用于实现查询取消metaRecordstring, unknown \| undefined可选字段可在其中填充关于该查询的附加信息在核心层的类型定义packages/query-core/src/types.ts#L144-L171中上述字段被明确建模为QueryFunctionContextTQueryKey, TPageParam常规查询时pageParam与direction以可选形式存在而在泛型参数TPageParam被具体化即无限查询场景时则变为必选字段。此外Infinite Queries无限查询的查询函数还会额外收到两个字段pageParam: TPageParam—— 获取当前页所使用的页码参数direction: forward | backward—— 当前页抓取的方向已废弃deprecated如需感知抓取方向官方建议在getNextPageParam/getPreviousPageParam返回的pageParam中自行带上方向信息而不要依赖此字段。基于signal字段可以实现真正的请求可取消性例如把context.signal透传给原生fetch组件卸载或缓存失效时请求即被中止详见 Query Cancellation 指南。client字段则让你在查询函数内也能调用client.getQueryData()等命令式 API 做数据联动。七、源码级延伸查询函数在 Solid Query 内部如何被驱动理解上述 API 后再看一眼tanstack/solid-query的实现能帮你更准确地建立心智模型。1. 选项作为 Accessor 求值。useQuery.ts 的实体会把options()结果包装为createMemo后交给useBaseQuery这意味着 options 内的每个响应式依赖变化都会驱动 observer 更新选项并重取export function useQueryTQueryFnData, TError, TData, TQueryKey extends QueryKey( options: UseQueryOptionsTQueryFnData, TError, TData, TQueryKey, queryClient?: AccessorQueryClient, ) { return useBaseQuery(createMemo(() options()), QueryObserver, queryClient) }顺带一提Solid 版 options 类型即AccessorQueryOptions...见 packages/solid-query/src/types.ts#L60-L65这正是传函数而非对象语法约束的类型来源。2. Promise 被接入 SolidJS 的createResource。在 useBaseQuery.ts 中查询函数产生的异步结果被包装进createResourcedata因此具备 SolidJS 资源的响应式与 Suspense 语义这也是 Solid 版没有独立suspense开关、data会自动触发挂起的原因。查询结果最终以一个 Proxy store 的形式返回读取state.data时实际走的是 resource 通道。3. 服务端渲染下的默认差异。同文件 useBaseQuery.ts 表明在服务端isServer会把默认retry强制置为false、throwOnError置为true——查询失败会直接抛错给 SSR 流而不是无限重试拖垮首屏。此外客户端默认关闭structuralSharinguseBaseQuery.ts改用 store 的reconcile机制配合可选的structuredClone做数据合并这是为 SolidJS 细粒度更新做的适配。如果只关心结果状态而不关心底层驱动细节直接使用state.status/state.isPending/state.isError/state.data等字段即可——返回对象是响应式的模板中直接读取即可自动更新。八、与其他概念的关系小结查询函数并非孤立概念它与以下主题紧密咬合建议按需延伸阅读均为本仓库 Solid 框架指南路径统一从仓库根目录出发Queries查询基础——useQuery的完整返回值与status/fetchStatus状态机Query Keys查询键——queryKey的结构化设计规范Query Cancellation查询取消——利用QueryFunctionContext.signal实现请求中止Infinite Queries无限查询——pageParam与方向字段的实战场景Default Query Function默认查询函数——当多个查询共享同一套取数逻辑时的收口方式Query Retries查询重试——查询函数抛错后的重试语义。掌握本文的 Promise 契约、主动抛错原则与QueryFunctionContext之后你便拥有了写出健壮、可复用、可取消查询函数所需的全部核心知识。【免费下载链接】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),仅供参考

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询