GraphQL vs TanStack Query
[!abstract] 一句话总结 GraphQL 是 API 的设计语言,TanStack Query 是前端的数据管家。它们不是竞争关系,而是可以互补的技术栈。
本质区别
| 维度 | GraphQL | TanStack Query |
|---|---|---|
| 类型 | API 查询语言规范 | 前端数据获取和状态管理库 |
| 解决什么问题 | 如何定义和查询 API | 如何管理异步数据状态 |
| 运行位置 | 服务端 + 客户端 | 仅客户端 |
| 数据源 | 定义 API 本身 | 可连接任何 API(REST、GraphQL、WebSocket) |
| 学习成本 | 较高(Schema、类型系统、解析器) | 中等(查询、缓存、失效策略) |
| 典型生态 | Apollo Server/Client、Relay、urql | React、Vue、Svelte、Angular、Solid |
核心类比
把它们想象成餐厅系统:
- GraphQL = 餐厅的菜单系统(定义你可以点什么、菜品的组合方式)
- TanStack Query = 你的管家(帮你点餐、记住你点过什么、自动续杯、处理异常)
两者配合:管家(TanStack Query)拿着菜单(GraphQL)帮你高效点餐。
架构关系图
graph TB
subgraph 前端应用
A[TanStack Query<br/>状态管理层] --> B[GraphQL 客户端<br/>Apollo/urql]
A --> C[REST/fetch]
end
B --> D[GraphQL API]
C --> E[REST API]
style A fill:#e1f5fe
style B fill:#f3e5f5
style C fill:#f3e5f5
层次说明:
- TanStack Query 处于状态管理层,负责缓存、重试、后台刷新、乐观更新
- GraphQL 客户端处于数据获取层,负责发送查询和解析响应
- 两者可以独立使用,也可以组合使用
使用方式对比
方式一:只用 GraphQL (Apollo Client)
import { useQuery, gql } from '@apollo/client';
const GET_USER = gql`
query GetUser($id: ID!) {
user(id: $id) { id name email }
}
`;
function User({ id }) {
const { loading, error, data } = useQuery(GET_USER, {
variables: { id }
});
// Apollo Client 自带缓存和状态管理
}
特点:一站式解决方案,缓存逻辑内置,但与 Apollo 生态绑定较深。
方式二:只用 TanStack Query (REST)
import { useQuery } from '@tanstack/react-query';
function User({ id }) {
const { isLoading, error, data } = useQuery({
queryKey: ['user', id],
queryFn: () => fetch(`/api/users/${id}`).then(r => r.json()),
});
// TanStack Query 管理缓存和状态
}
特点:框架无关,灵活可控,但需要自己处理数据获取细节。
方式三:两者结合使用
import { useQuery } from '@tanstack/react-query';
import { request, gql } from 'graphql-request';
const GET_USER = gql`
query GetUser($id: ID!) {
user(id: $id) { id name email }
}
`;
function User({ id }) {
const { isLoading, error, data } = useQuery({
queryKey: ['user', id],
queryFn: () => request('/graphql', GET_USER, { id }),
});
// TanStack Query 管理状态,GraphQL 定义查询
}
特点:最佳实践之一,结合 GraphQL 的查询能力和 TanStack Query 的状态管理能力。
功能特性对比
| 特性 | Apollo Client | TanStack Query |
|---|---|---|
| 缓存 | ✅ 归一化缓存(自动) | ✅ 查询键缓存(手动配置) |
| 后台刷新 | ✅ 轮询 | ✅ 自动后台刷新 |
| 乐观更新 | ✅ 支持 | ✅ 支持 |
| 重试机制 | ✅ 基础 | ✅ 高级(指数退避) |
| 离线支持 | ✅ 有 | ✅ 有 |
| DevTools | ✅ Apollo DevTools | ✅ TanStack Query DevTools |
| 框架支持 | React、Vue、Angular | React、Vue、Svelte、Angular、Solid |
| 学习曲线 | 较陡 | 中等 |
选择决策树
graph TD
A[开始选择] --> B{API 类型?}
B -->|GraphQL| C{需要深度缓存?}
B -->|REST| D[TanStack Query]
C -->|是| E[Apollo Client / Relay]
C -->|否| F[TanStack Query + graphql-request]
E --> G{需要跨框架?}
F --> G
G -->|是| H[TanStack Query]
G -->|否| I[根据团队熟悉度选择]
style D fill:#c8e6c9
style E fill:#c8e6c9
style F fill:#c8e6c9
style H fill:#c8e6c9
选择建议
| 场景 | 推荐方案 | 理由 |
|---|---|---|
| 项目已用 GraphQL,需要强大缓存 | Apollo Client 或 Relay | 一站式解决方案,归一化缓存强大 |
| 项目用 REST,想优化数据获取 | TanStack Query | 简单易用,自动处理 loading/error 状态 |
| 项目用 GraphQL,想更灵活的缓存控制 | TanStack Query + graphql-request | 解耦缓存逻辑,便于跨框架 |
| 新项目,团队熟悉 React 生态 | TanStack Query + REST 或 GraphQL | 学习成本低,社区活跃 |
| 需要跨框架(Vue/Svelte/Angular) | TanStack Query | 框架无关,统一 API |
| 移动端 React Native | Apollo Client 或 TanStack Query | 两者都支持良好 |
各自的优势
GraphQL 的优势
- ✅ 精确获取数据,避免过度获取/不足获取
- ✅ 强类型 Schema,API 自文档化
- ✅ 一次请求获取关联数据
- ✅ 版本演进无需破坏性变更
TanStack Query 的优势
- ✅ 框架无关,一套 API 多端复用
- ✅ 自动后台刷新和缓存失效
- ✅ 简洁的 API,学习成本低
- ✅ 强大的 DevTools 支持
- ✅ 乐观更新和离线支持
常见误解
[!danger] ❌ 误以为:GraphQL 和 TanStack Query 是竞争关系 ✅ 实际上:它们是不同层次的技术,可以互补使用。GraphQL 定义 API,TanStack Query 管理状态。
[!danger] ❌ 误以为:用了 GraphQL 就必须用 Apollo Client ✅ 实际上:你可以用 TanStack Query + graphql-request,或者 urql 等其他客户端。
[!danger] ❌ 误以为:TanStack Query 只能用于 REST ✅ 实际上:TanStack Query 可以连接任何异步数据源,包括 GraphQL、WebSocket、甚至本地函数。
实战组合推荐
组合一:全栈 GraphQL 方案
前端:Apollo Client / Relay
后端:Apollo Server / GraphQL Yoga
适用:大型项目,需要深度缓存和实时更新
组合二:灵活混合方案
前端:TanStack Query + graphql-request
后端:Apollo Server / GraphQL Yoga
适用:需要跨框架支持,或想要更灵活的缓存控制
组合三:REST 优化方案
前端:TanStack Query
后端:REST API + OpenAPI 规范
适用:传统 REST API,想优化数据获取体验
关联笔记
前置知识:GraphQL · REST API · React 同族概念:Apollo Client · Relay · urql · SWR 应用场景:前后端分离 · 单页应用 · 移动应用开发