首页
/ TanStack Query lit-query 类型参考:QueriesControllerOptions 详解与 createQueriesController 选项机制

TanStack Query lit-query 类型参考:QueriesControllerOptions 详解与 createQueriesController 选项机制

2026-09-07 17:43:24作者:田桥桑Industrious

本篇技术指南围绕 @tanstack/lit-query 的类型别名 QueriesControllerOptions 展开,它是 createQueriesController 所接受的选项类型。读完本文,你能准确理解该类型的两层结构(Accessor 包装 + CreateQueriesControllerOptions 本体)、两个类型参数的含义与默认值,并掌握在 Lit 组件中编写静态/动态并行查询、使用 combine 聚合多查询结果的完整用法。

类型定义与出处

QueriesControllerOptions 的完整定义为:

type QueriesControllerOptions<TQueryOptions, TCombinedResult> = Accessor<CreateQueriesControllerOptions<TQueryOptions, TCombinedResult>>;

该类型定义于 packages/lit-query/src/types.ts,源码中带有明确的文档注释:"Accessor-wrapped options accepted by createQueriesController"(供 createQueriesController 使用的、被 Accessor 包装的选项)。

从定义结构看,它由两个正交部分组成:

  1. 外层 Accessor<T> 包装:允许调用方直接传入选项对象,或传入一个零参 getter 函数,让选项跟随 Lit 组件的响应式状态在宿主更新时被重新求值;
  2. 内层 CreateQueriesControllerOptions<TQueryOptions, TCombinedResult>:真正的选项本体,包含 queries(查询列表)与可选的 combine(结果聚合函数)两个字段。

这种"双层可访问"设计是 lit-query 控制器 API 的统一约定,Accessor 的类型本体与运行时读取逻辑见 packages/lit-query/src/accessor.ts

export type Accessor<T> = T | (() => T)

export function readAccessor<T>(value: Accessor<T>): T {
  return typeof value === 'function' ? (value as () => T)() : value
}

类型参数

TQueryOptions

约束为 Array<any>,默认值 Array<any>。它是传入 queries查询选项数组(或元组)的静态类型,是结果类型推断的输入。源码中每个数组元素对应 CreateQueriesInput(即 @tanstack/query-coreQueryObserverOptions 形态),见 packages/lit-query/src/createQueriesController.ts

TCombinedResult

默认值为 CreateQueriesResults<TQueryOptions>。即:如果不显式指定该类型参数(也未传 combine),返回结果就是一个按输入顺序推断出的查询结果元组;如果传了 combine,它则是 combine 的返回值类型。CreateQueriesResults 通过递归映射每个查询项的 queryFn/select/initialData 等信息推断出对应的 QueryObserverResult,其递归推断受 MAXIMUM_DEPTH = 20 深度限制,超限后退化为 Array<QueryObserverResult>,见 packages/lit-query/src/createQueriesController.ts

内层选项 CreateQueriesControllerOptions 的字段

剥去 Accessor 包装后,实际可配置的字段定义在 packages/lit-query/src/createQueriesController.ts

export type CreateQueriesControllerOptions<
  TQueryOptions extends Array<any> = Array<any>,
  TCombinedResult = CreateQueriesResults<TQueryOptions>,
> = {
  /** Query options to observe, or a getter that returns the current options. */
  queries: Accessor<
    | readonly [...CreateQueriesOptions<TQueryOptions>]
    | readonly [
        { [K in keyof TQueryOptions]: GetCreateQueriesInput<TQueryOptions[K]> }
      ]
  >
  /** Optional function that combines the query result array into one value. */
  combine?: (result: CreateQueriesResults<TQueryOptions>) => TCombinedResult
}

各字段说明:

  • queries(必填):要观察的查询选项列表。注意它本身也是 Accessor 类型——即使外层 options 是静态对象,queries 也可以单独写成 getter 以跟随响应式状态;反之亦然。两种嵌套组合都支持:
    • options 是对象 + queries 是 getter;
    • options 本身是 getter(此时整个选项在宿主更新时重新读取)。
  • combine(可选):将"查询结果数组"折叠为单个派生值的函数。不传时,QueriesControllerOptions 的返回类型即 TCombinedResult 默认值 CreateQueriesResults<TQueryOptions>(结果元组);传入时,其返回值即为最终类型。

从源码结构看,queries 的类型签名是一个 readonly 的联合类型:既接受递归映射后的数组形式(CreateQueriesOptions),也接受按 keyof TQueryOptions 映射的元组形式,这正是"输入是元组时结果也推断为对应位置元组"的类型机制来源。

使用示例

静态并行查询 + combine 聚合

以下示例来自 packages/lit-query/src/createQueriesController.ts 中的官方 @example 注释,options 以静态对象传入(合法 Accessor 分支之一):

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 html`<p>Projects: ${dashboard.projects.length}</p>`
  }
}

动态查询列表(options 作为 getter)

当查询数量/内容依赖宿主响应式字段时,将整个 options 写成 getter。该用法收录于 docs/framework/lit/guides/parallel-queries.md

import { LitElement, html } from 'lit'
import { createQueriesController } from '@tanstack/lit-query'

class UsersDetails extends LitElement {
  static properties = {
    userIds: { attribute: false },
  }

  userIds: Array<string> = []

  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 html`<li>Loading...</li>`
          if (query.isError) return html`<li>Error loading user</li>`

          return html`<li>${this.userIds[index]}: ${query.data.name}</li>`
        })}
      </ul>
    `
  }
}

结果顺序与输入查询顺序严格一致;若 queries 数组中出现重复的 queryKey,这些条目会共享缓存数据——如果每行渲染需要独立查询状态,应先去重。

源码级机制:选项在运行期如何被消费

QueriesControllerOptions 不只是类型层面的约定,QueriesController 类(packages/lit-query/src/createQueriesController.ts)在运行时对其做了如下消费:

1. 两层 Accessor 的解析resolveQueriesOptionsreadAccessor(optionsAccessor) 得到内层选项对象,再 readAccessor(resolvedOptions.queries) 得到实际查询数组,随后逐项经过 client.defaultQueryOptions(...) 应用全局默认配置并打上 _optimisticResults: 'optimistic' 标记(乐观结果,避免初次订阅时因缓存未就绪而抖动),最后交给 QueriesObserver 建立对多个查询的观察,见 packages/lit-query/src/createQueriesController.ts

2. 何时重新读取选项shouldRefreshOnHostUpdate 的判定逻辑直接对应类型定义中两处 Accessor 位置:options 是函数,或 options.queries 是函数,任一成立,控制器就会在宿主更新(onHostUpdate)时重新解析选项并调用 observer.setQueries 同步查询列表,见 packages/lit-query/src/createQueriesController.ts。这就是"类型上允许函数、运行时保证响应式刷新"的闭环。

3. QueryClient 解析与占位结果。未显式传入 queryClient 时,控制器从最近的 QueryClientProvider 上下文解析;若客户端尚未就绪,会先为每个查询生成占位 QueryObserverResult(有 initialData 时直接映射为成功态),并在构造完成后重试初始化,见 packages/lit-query/src/createQueriesController.tsdocs/framework/lit/reference/functions/createQueriesController.md 中对 queryClient 参数的说明。

4. 返回值的形态createQueriesController 返回 QueriesResultAccessor<TCombinedResult>——一个可调用函数,同时暴露 current 属性与 destroy() 方法;combine 的返回值会经过 replaceEqualDeep 做深比较去重,只有实际内容变化才触发宿主更新,见 packages/lit-query/src/createQueriesController.ts

行为层面的覆盖可参考测试文件 packages/lit-query/src/tests/queries-controller.test.ts 与类型推断测试 packages/lit-query/src/tests/type-inference.test.ts

与其他类型的关系

小结

QueriesControllerOptions<TQueryOptions, TCombinedResult> 是 lit-query 并行查询入口 createQueriesController 的类型契约:外层 Accessor 决定了"选项可以静态传入、也可以随宿主状态动态求值",内层 queries + combine 决定了"观察哪些查询、结果如何聚合",TQueryOptionsTCombinedResult 两个类型参数则通过元组递归推断把输入选项映射为精确的返回类型(无 combine 时为结果元组,有 combine 时为其返回值)。在编写 Lit 组件的动态并行查询时,理解这层结构即可正确使用静态选项、getter 选项与结果聚合三种模式。

登录后查看全文
热门项目推荐
相关项目推荐