执行契约
- 适用场景
- 目标路由进入前应预热 Query 缓存,并由组件继续观察同一数据。
- 不要使用
- 首屏不需要、用户很少访问或成本很高的数据不应无条件阻塞 Loader。
- 前置检查
- 建立共享 queryOptions 工厂,并确保 Loader deps 包含影响 Query Key 的所有路由输入。
- 验证结果
- 导航期间只触发一个网络请求,组件命中相同缓存;变更参数时产生不同且可预测的 Key。
- 失败模式
- 若组件立即重复请求,比较 Loader 与组件的完整 Query Key、新鲜度和 QueryClient 实例。
- 安全边界
- 预取不会隐藏响应;不要将未经授权的数据放入可序列化缓存或 SSR 负载。
前置集成
在 getRouter 内创建 QueryClient,将它放入 Router context,然后调用 setupRouterSsrQueryIntegration。getRouter 在 SSR 中按请求执行;不要把 QueryClient 提升到模块级单例。
const queryClient = new QueryClient()
const router = createRouter({
routeTree,
context: { queryClient },
})
setupRouterSsrQueryIntegration({ router, queryClient })阻塞式关键数据
对渲染必需的数据,从 Loader 返回 ensureQueryData Promise。组件用相同 queryOptions 调用 useSuspenseQuery。这样 Router 等待导航,Query 管理缓存,SSR 集成完成脱水与恢复。
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} />
},
})验证清单
确认 Router 根 context 声明了 QueryClient 类型;确认服务端没有跨请求共享 QueryClient;确认 Loader 与组件的 queryKey 完全一致;运行 SSR 页面并检查客户端未重复首屏请求。