← [ BACK_TO_NOTES ]

Apollo Client 在 GraphQL 中的角色

Apollo Client 是面向 GraphQL 的客户端数据层:负责发送 operation、跟踪加载/错误状态,并围绕 GraphQL 实体维护 normalized cache、Mutation reconciliation 与实时更新。

#concept #growing #frontend #graphql #react

[!info] related notes

Apollo Client 在 GraphQL 中的角色

一句话定义

Apollo Client 是一个理解 GraphQL operation 和实体关系的客户端数据层。

它不等于 GraphQL;GraphQL 是规范与执行模型,Apollo Client 是其中一种客户端实现。

它主要负责什么

在 React 中,一次查询大致经过:

React component
   ↓
Apollo Client
   ↓ GraphQL Query + variables
GraphQL Server
   ↓
data / errors
   ↓
Apollo Cache
   ↓
React re-render

它通常提供:

  • Query / Mutation / Subscription 执行;
  • loading / data / error 状态;
  • variables 管理;
  • normalized cache;
  • mutation 后缓存 reconciliation;
  • refetch 与 cache policy;
  • Subscription payload 与缓存的整合。

与 TanStack Query 的关键区别

两者都能管理 server state,但抽象中心不同。

TanStack Query 主要认识:

queryKey → queryFn → cached result

例如:

['user', id]

Apollo Client 还理解 GraphQL 返回的实体身份与字段关系:

User:1
Post:10
Diagnosis:d9

同一个实体出现在多个 Query 中时,可以共享同一 normalized entity。

因此:

TanStack Query 更偏 query-centric cache;Apollo Client 更偏 entity/graph-centric cache。

这是心智模型上的核心差异,而不是谁能不能“直接改缓存”。

TanStack Query 也能直接修改缓存

不要把 TanStack Query 简化为:

Mutation
→ invalidate
→ refetch

它同样可以:

setQueryData
setQueriesData
optimistic update
mutation response → patch cache

invalidateQueries + refetch 常见,是因为当客户端不能可靠推导服务端最终状态时,重新取 authority 更稳妥;不是因为 TanStack Query 没有本地更新能力。

Query 变量

query GetUser($id: ID!) {
  user(id: $id) {
    id
    name
  }
}

$id 属于 operation 的变量,而不是把字符串直接拼进 GraphQL 文本。

这使 operation 可以复用,也让参数经过 GraphQL 类型校验。

Mutation 后 Apollo 到底怎样自动更新

一个容易误解的说法是:

“Apollo 根据 Mutation 入参知道用户改了什么,所以自动改本地缓存。”

默认机制更准确的是:Apollo 主要根据 Mutation 的响应结果做 normalization 和 merge,而不是仅凭输入参数猜测服务端最终状态。

例如缓存已有:

User:42
name = "Sean"

Mutation:

mutation {
  updateUser(id: "42", input: { name: "Arlon" }) {
    id
    name
  }
}

服务端返回可识别实体:

__typename = User
id = 42
name = Arlon

Apollo 可以识别:

User:42

然后把 name merge 成 Arlon。所有引用 User:42 的查询视图都有机会同步更新。

如果 Mutation 只返回 success

updateUser(...) {
  success
}

Apollo 并不能自动知道:

哪个 User 变了?
哪些字段变了?
哪些列表成员关系变了?

此时通常选择:

1. 让 Mutation 返回更新后的实体
2. cache.modify / writeFragment / update
3. refetchQueries

Optimistic Update 是另一回事

如果客户端希望点击后立即显示新值,可以用 mutation 输入构造 optimisticResponse 或手动 patch cache:

用户点击
→ 先乐观修改本地 projection
→ 服务端响应
→ 用真实结果校正

这是显式的客户端策略,不应和“Mutation 响应实体被 normalized merge”混成同一个机制。

本客户端写入与外部变化是两个问题

当前客户端自己发起 Mutation

如果当前页面自己执行:

Mutation
→ Server update
→ 返回更新后的 Entity
→ Apollo normalized merge
→ 当前 UI 更新

通常不需要为了这一次变化再建立 Subscription。

当前客户端没有发起变化

例如:

用户 A / 设备 A / 后台任务
→ 修改服务器状态

用户 B 当前页面
→ 没有发起这次 Mutation

用户 B 的 Apollo Cache 不会凭空知道服务器已经变化。

需要某种“变化通知”机制:

GraphQL Subscription
SSE / WebSocket event
Polling
focus/reconnect refetch

如果使用 GraphQL Subscription:

Server event
→ Subscription payload
→ identify Entity
→ normalized cache merge / explicit update
→ UI re-render

所以:

Mutation reconciliation 解决“我刚刚自己写了以后,本地怎么跟上”;Subscription 解决“别人或服务器改了以后,我怎么及时知道”。

单用户是否就完全没有同步问题

也不能绝对这么说。

即使只有一个用户,也可能存在:

  • 同一账号多个 tab / 设备;
  • 后台任务修改状态;
  • 定时任务或 Agent 修改数据;
  • Mutation 引发服务端计算,影响多个实体或聚合字段;
  • 新增/删除导致列表 membership 变化;
  • Mutation response 没有返回全部受影响数据。

因此单用户下,“当前客户端自己修改的单个实体字段”通常最容易直接同步;但涉及外部变化、派生状态、集合关系或后台处理时,仍可能需要 refetch 或实时事件。

为什么 Mutation 后需要额外思考

服务端写成功,不代表所有已缓存 Query 都自动得到正确的新结果。

如果返回了具有稳定 identity 的实体,Apollo 可能自动合并实体字段;但新增/删除列表元素、复杂派生查询等场景仍可能需要:

cache update
refetch
或更明确的 field policy

因此客户端一致性仍然是工程问题,不会因为使用 GraphQL 自动消失。

边界

Apollo Client 不应该成为业务 durable truth。

Database / Server state = authority
Apollo Cache            = client projection

缓存可以丢弃、重建、重新拉取;真正业务状态仍由服务端拥有。

学习重点

先掌握五个问题:

  1. 一次 GraphQL Query 怎样进入 Apollo Client;
  2. Apollo 为什么需要 normalized cache;
  3. Mutation 响应怎样通过 entity identity 更新 cache;
  4. 什么时候应该直接 patch,什么时候应该 refetch;
  5. 当前客户端写入与外部实时变化为什么需要不同同步机制。
创建于 2026/9/11 更新于 2026/9/15
Digital Biome
Access

键盘导航

j / k / ↑ / ↓
选择对象
Enter / o
打开选择
/ 或 Ctrl+K
全站搜索
i / f / 1–4
筛选当前列表
g → h / l / p / t / a
首页 / 笔记 / 项目 / 工具 / About
r → 1–9
打开所选对象的关联
u
返回上一页
Esc
关闭浮层 / 取消命令;输入框内清空文本