浏览文档目录
Agent docs/query/loader-prefetch
recipe

在路由 Loader 中预取 Query

为 SSR 创建请求级 QueryClient,并让 Loader 与 useSuspenseQuery 使用相同 queryOptions。

View raw Markdown
CONTRACT

执行契约

适用场景
目标路由进入前应预热 Query 缓存,并由组件继续观察同一数据。
不要使用
首屏不需要、用户很少访问或成本很高的数据不应无条件阻塞 Loader。
前置检查
建立共享 queryOptions 工厂,并确保 Loader deps 包含影响 Query Key 的所有路由输入。
验证结果
导航期间只触发一个网络请求,组件命中相同缓存;变更参数时产生不同且可预测的 Key。
失败模式
若组件立即重复请求,比较 Loader 与组件的完整 Query Key、新鲜度和 QueryClient 实例。
安全边界
预取不会隐藏响应;不要将未经授权的数据放入可序列化缓存或 SSR 负载。
01

前置集成

在 getRouter 内创建 QueryClient,将它放入 Router context,然后调用 setupRouterSsrQueryIntegration。getRouter 在 SSR 中按请求执行;不要把 QueryClient 提升到模块级单例。

src/router.tsxtsx
const queryClient = new QueryClient()
const router = createRouter({
  routeTree,
  context: { queryClient },
})

setupRouterSsrQueryIntegration({ router, queryClient })
02

阻塞式关键数据

对渲染必需的数据,从 Loader 返回 ensureQueryData Promise。组件用相同 queryOptions 调用 useSuspenseQuery。这样 Router 等待导航,Query 管理缓存,SSR 集成完成脱水与恢复。

src/routes/issues/index.tsxtsx
const issuesQuery = queryOptions({
  queryKey: ['issues'],
  queryFn: getIssues,
})

export const Route = createFileRoute('/issues/')({
  loader: ({ context }) =>
    context.queryClient.ensureQueryData(issuesQuery),
  component: () => {
    const { data } = useSuspenseQuery(issuesQuery)
    return <IssueList issues={data} />
  },
})
03

验证清单

确认 Router 根 context 声明了 QueryClient 类型;确认服务端没有跨请求共享 QueryClient;确认 Loader 与组件的 queryKey 完全一致;运行 SSR 页面并检查客户端未重复首屏请求。

PRIMARY SOURCEShttps://tanstack.com/router/latest/docs/integrations/query