---
id: query.router-or-query
kind: decision
product: query
framework: react
locale: zh-CN
revision: 2
sourceCheckedOn: "2026-08-03"
versionRange: "Query ^5 / Router ^1"
verifiedAgainst: "@tanstack/react-query@5.101.4 + @tanstack/react-router@1.170.18"
contentModel: 2
packages:
  - "@tanstack/react-query"
  - "@tanstack/react-router"
tasks:
  - "choose-loader"
  - "choose-query"
  - "data-loading"
sourceRefs:
  - "https://tanstack.com/router/latest/docs/integrations/query"
  - "https://tanstack.com/query/latest/docs/framework/react/overview"
---

# Router Loader 还是 Query？

> 根据导航关键性和数据生命周期选择 Loader、Query 或两者组合。

## 执行契约

- **适用场景:** 需要决定数据由路由生命周期直接拥有，还是进入具有独立缓存生命周期的 Query。
- **不要使用:** 不要用这项决策处理纯客户端 UI 状态；它既不属于 Loader，也不属于 Query。
- **前置检查:** 记录数据是否跨路由复用，以及是否需要失效、轮询、重试或乐观更新。
- **验证结果:** 输出明确的所有者、缓存策略和重新获取触发点；如果组合使用，必须共享一份 queryOptions。
- **失败模式:** 重复请求通常来自 Loader 和组件各自维护不同 Key 或请求实现；先统一契约再调整 staleTime。
- **安全边界:** 两种加载方式都可能触达服务端数据；数据源必须独立执行权限检查。

## 决策规则

数据决定路由能否显示、需要 redirect/notFound，或应在导航完成前准备时，使用 Loader。数据需要长期缓存、后台重新获取、失效、重试或乐观更新时，使用 Query。

## 组合方式

常用组合是在 Loader 中调用 queryClient.ensureQueryData，让 Router 协调导航，让 Query 拥有缓存。前提是 QueryClient 已经被注入 Router context。

File: `src/routes/issues/index.tsx`

```tsx
export const Route = createFileRoute('/issues/')({
  loader: ({ context }) =>
    context.queryClient.ensureQueryData(issuesQueryOptions),
  component: IssuesPage,
})
```

## 不要做

不要让 Loader 和 Query 各自用不同 key 重复请求同一数据。不要在没有缓存需求时为了“标准化”强行加入 Query。

## Official sources

- https://tanstack.com/router/latest/docs/integrations/query
- https://tanstack.com/query/latest/docs/framework/react/overview
