十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

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

@tanstack/lit-query 的 CreateQueriesControllerOptions 类型详解:动态多查询控制器中 queries 与 combine 的完整用法

@tanstack/lit-query 的 CreateQueriesControllerOptions 类型详解:动态多查询控制器中 queries 与 combine 的完整用法 tanstack/lit-query 的 CreateQueriesControllerOptions 类型详解动态多查询控制器中 queries 与 combine 的完整用法【免费下载链接】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本文聚焦 Lit Querytanstack/lit-query中createQueriesController所接受的核心选项类型CreateQueriesControllerOptions完整解析其两个属性queries与combine的类型签名、类型参数推导规则及可运行的使用示例。读完本文你将理解如何在 Lit 组件中同时订阅一组静态或响应式的查询把多查询结果组合成单个派生值并能从仓库源码中印证queries接受 getter、结果元组类型推断、占位结果等底层机制。类型定义与定位CreateQueriesControllerOptions是createQueriesController接受的选项类型完整定义位于 createQueriesController.tsexport type CreateQueriesControllerOptions TQueryOptions extends Arrayany Arrayany, TCombinedResult CreateQueriesResultsTQueryOptions, { /** Query options to observe, or a getter that returns the current options. */ queries: Accessor | readonly [...CreateQueriesOptionsTQueryOptions] | readonly [ ...{ [K in keyof TQueryOptions]: GetCreateQueriesInputTQueryOptions[K] }, ] /** Optional function that combines the query result array into one value. */ combine?: (result: CreateQueriesResultsTQueryOptions) TCombinedResult }该类型的注释明确了两个关键语义同样出现在官方参考文档 CreateQueriesControllerOptions.md 中queries可以是一个静态列表也可以是一个返回当前列表的 getter从而让查询列表跟随 Lit 组件的响应式状态变化combine可以把查询结果数组重塑为单个值作为返回访问器accessor的最终输出。从类型签名看TQueryOptions的约束是数组文档中的extends any[]源码中写作extends Arrayany默认值为any[]TCombinedResult的默认值是CreateQueriesResultsTQueryOptions——即当调用方不提供combine时访问器直接返回按输入顺序推导出的结果元组。类型参数详解TQueryOptionsTQueryOptions是传入queries的查询选项元组类型其每个元素决定对应位置结果的类型。仓库中围绕它定义了两组相互对应的推导类型CreateQueriesOptions把输入元组映射为“控制器输入”元组用于约束queries属性可接受的形状CreateQueriesResults把输入元组映射为QueryObserverResult结果元组是combine回调参数与默认TCombinedResult的类型来源。两者都是递归的变长元组映射并带有MAXIMUM_DEPTH 20的深度保护见 第 45 行 与 第 134 行当元组递归推导超过 20 层时退化为宽泛的ArrayCreateQueriesInputForController/ArrayQueryObserverResult避免 TypeScript 编译性能问题。从源码结构看这意味着超长的动态查询列表会损失逐元素精度而常规长度数十个以内的元组可以保持完整的逐位类型。单个元素的输入类型由 GetCreateQueriesInput 推导。它支持多种写法通过queryOptions(...)等 helper 标注的queryFnData/data元组信息[queryFnData, error?]、[queryFnData, error, data]这类显式元组标注最常见的{ queryFn, select?, throwOnError? }对象字面量此时TQueryFnData从queryFn推断、TData优先取select的输出类型。结果侧则由 GetDefinedOrUndefinedCreateQueriesResult 推导其中最值得注意的规则是当查询提供了initialData且其类型能覆盖TData时对应位置的结果类型会从QueryObserverResult收紧为DefinedQueryObserverResult即data不为undefined。这保证combine回调里可以安全地直接访问带initialData的查询的数据。TCombinedResultTCombinedResult表示最终访问器返回的值。默认值CreateQueriesResultsTQueryOptions意味着不提供combine时返回按输入顺序排列的结果元组提供combine时由combine的返回类型决定。仓库的类型测试 type-inference.test.ts 验证了这一行为const tupleResult createQueriesController( host, { queries: [ { queryKey: [type-inference, tuple-number] as const, queryFn: async () 1, }, { queryKey: [type-inference, tuple-string] as const, queryFn: async () x, }, ] as const, }, client, ) const tupleData expectTupleResult(tupleResult()) expectTypeOf(tupleData[0].data).toEqualTypeOfnumber | undefined() expectTypeOf(tupleData[1].data).toEqualTypeOfstring | undefined()即两个查询的结果构成[QueryObserverResultnumber, QueryObserverResultstring]元组顺序与输入一致、逐元素带类型。同一测试文件还验证了combine分支combinedResult().first为number | undefined以及initialData收紧为DefinedQueryObserverResult的情形第 81-L119 行。queries 属性静态列表或响应式 getterqueries的类型是Accessor...而Accessor在 accessor.ts 中定义为export type AccessorT T | (() T)也就是“值或零参 getter”二选一。读取逻辑在 readAccessor是函数就调用否则原样返回。这直接支撑了文档中“queriescan be a static list or a getter that returns the current list”的说明。getter 何时被重新求值答案在QueriesController的 shouldRefreshOnHostUpdateprivate shouldRefreshOnHostUpdate(): boolean { if (typeof this.options function) { return true } return typeof this.options.queries function }只要整个options是函数或其中queries是函数控制器就会在每次宿主组件更新onHostUpdate时重新读取查询列表并调用observer.setQueries(...)见 refreshOptions。因此典型的动态用法是把整个 options 写成 getter让查询列表依赖组件属性import { LitElement, html } from lit import { createQueriesController } from tanstack/lit-query class UsersDetails extends LitElement { static properties { userIds: { attribute: false }, } userIds: Arraystring [] private readonly users createQueriesController(this, () ({ queries: this.userIds.map((id) ({ queryKey: [user, id], queryFn: () fetchUserById(id), })), })) render() { const userQueries this.users() return html ul ${userQueries.map((query, index) { if (query.isPending) return htmlliLoading.../li if (query.isError) return htmlliError loading user/li return htmlli${this.userIds[index]}: ${query.data.name}/li })} /ul } }该示例与仓库指南 parallel-queries.md 中“Dynamic Parallel Queries”一节一致。仓库测试 queries-controller.test.ts 中的DeferredFieldsQueriesHost进一步覆盖了 getter 场景的一个边界组件字段this.ids在控制器构造之后才初始化。测试断言了这种“延迟字段”模式下 getter 仍能在后续更新中被正确重新求值与源码中readCurrent的重试机制构造期读取失败时先记录错误微任务后重试见 第 621-L648 行相对应。combine 属性把结果数组合并为单个派生值combine是可选的函数属性combine?: (result: CreateQueriesResultsTQueryOptions) TCombinedResult参数是完整的结果元组返回任意派生值。省略它时访问器返回结果数组本身提供它时访问器返回combine的输出从而把“多个查询”在组件层面折叠成“一个数据对象”非常适合仪表盘式组件。官方 JSDoc 中的完整示例第 676-L699 行import { LitElement, html } from lit import { createQueriesController } from tanstack/lit-query class DashboardView extends LitElement { private readonly dashboard createQueriesController(this, { queries: [ { queryKey: [stats], queryFn: fetchStats }, { queryKey: [projects], queryFn: fetchProjects }, ], combine: ([stats, projects]) ({ stats: stats.data, projects: projects.data ?? [], isPending: stats.isPending || projects.isPending, }), }) render() { const dashboard this.dashboard() return htmlpProjects: ${dashboard.projects.length}/p } }注意combine回调接收到的每个元素都是完整QueryObserverResult含isPending/isError/dataUpdatedAt等状态字段所以合并时不仅拿数据还能聚合各查询的加载与错误状态。指南中的另一个变体parallel-queries.md 的“Combining Results”演示了把多个查询的状态聚合为单一isPending/isError标志的写法。combine的返回值并非直接触发更新的“原始值”。从源码看createResult 在处理时做了两件事先用trackResult第 580-L602 行把每个结果套上属性访问追踪使组件只访问过的结果属性参与 Lit 的更新判断再用replaceEqualDeep对 combine 输出做深比较换值只有内容真正变化时才让引用改变从而避免重复渲染。运行时行为options 如何被消费理解类型签名之外CreateQueriesControllerOptions在运行时的消费链路同样清晰均在 createQueriesController.ts 内解析与默认值resolveQueriesOptions 先经readAccessor依次解出 options 与 queries然后对每个查询调用client.defaultQueryOptions(...)应用QueryClient级默认配置如全局retry、staleTime并把内部标记_optimisticResults置为optimistic使新键的初始状态可以立即以乐观方式产生。创建观察者解析结果交给 query-core 的QueriesObserver构造时传入{ combine }见 第 412-L414 行客户端变化或查询列表变化时通过refreshOptions调setQueries热更新。占位结果在QueryClientProvider的 client 尚未绑定、或宿主字段尚未就绪的早期读取窗口createPlaceholderQueryObserverResult 会为每个查询生成 pending 占位结果——若该查询带initialData占位结果直接以status: success呈现初始数据没有 client 时占位结果的refetch会 reject 缺失 client 的错误。测试 LC-QUERIES-01 验证了 provider 连接前先读到[{ status: pending, ... }, ...]占位值、连接后平滑过渡到success的完整时序。客户端解析createQueriesController(host, options, queryClient?)的第三个参数可选。省略时基类 BaseController 通过lit/context的ContextEvent从最近的QueryClientProvider解析 client并在 context 值变化时触发onQueryClientChanged重建观察者。返回值createQueriesController 返回QueriesResultAccessorTCombinedResult即可调用函数且带current属性与destroy()方法的访问器类型见 第 218-L222 行destroy会把控制器从宿主摘除并退订所有查询观察者。完整示例与注意事项把类型、getter 用法与combine放在一起的最小完整组件import { LitElement, html } from lit import { createQueriesController } from tanstack/lit-query class DashboardView extends LitElement { static properties { teamId: { attribute: false } } teamId default // getter 形式teamId 变化时 queries 会被重新求值 private readonly dashboard createQueriesController(this, () ({ queries: [ { queryKey: [stats, this.teamId], queryFn: () fetchStats(this.teamId), }, { queryKey: [projects, this.teamId], queryFn: () fetchProjects(this.teamId), }, ], combine: ([stats, projects]) ({ activeUsers: stats.data?.activeUsers ?? 0, projects: projects.data ?? [], isPending: stats.isPending || projects.isPending, isError: stats.isError || projects.isError, }), })) render() { const dashboard this.dashboard() if (dashboard.isPending) return htmlLoading... if (dashboard.isError) return htmlUnable to load dashboard return html pTotal projects: ${dashboard.projects.length}/p pActive users: ${dashboard.activeUsers}/p } }使用CreateQueriesControllerOptions时的几点实践建议均来自仓库文档与源码行为结果顺序与输入顺序严格一致动态列表渲染时可以按索引对齐userIds[index]之类的宿主状态见 parallel-queries.md 的说明。重复 queryKey 会共享缓存数据queries数组中出现相同 key 的条目会共享同一份缓存如果每行 UI 需要独立的查询状态应先对 key 去重或让 key 携带行级标识。需要类型精度时用as const或queryOptions元组推断对as const字面量与queryOptionshelper 标注的数据最为精确对照 type-inference.test.ts 的写法。组件卸载前调用accessor.destroy()或在不需要显式 client 时依赖宿主连接生命周期以退订所有查询观察者。小结CreateQueriesControllerOptions虽然只是一个两属性的选项类型但它串联了 Lit Query 多查询能力的全部关键设计queries的Accessor包装让查询列表既可以是静态元组也可以是跟随宿主状态的 gettercombine让多查询结果在组件内折叠为单一派生值而CreateQueriesResults的元组推导让每个查询的数据与状态类型一路精确传递到render()。结合 packages/lit-query/src/createQueriesController.ts 的实现与 packages/lit-query/src/tests/queries-controller.test.ts、packages/lit-query/src/tests/type-inference.test.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),仅供参考
返回列表