Apollo 缓存:规范化与更新
了解 Apollo 的规范化 InMemoryCache,并在变更后更新缓存数据而无需重新获取
Apollo 缓存:规范化与更新 是 CoddyKit 上的免费 React Academy 课时。 这是第 4 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 React Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 React Academy 课程共包含 4 节课。
InMemoryCache 如何规范化数据
InMemoryCache 会根据由 __typename 和 id 组成的缓存键存储每个对象,例如 User:1、Post:42。当查询返回 id 为 "1" 的 User 时,无论有多少个不同查询包含它,都会在此键下只存储一份。
之后任何获取同一个 User:1 的查询都会从这条缓存记录中读取,从而确保所有组件看到相同的数据。
自动同步不同查询的数据
当变更返回更新后的 User:1 对象时,Apollo 会将其写入 User:1 缓存记录。所有包含 User:1 的活动查询都会自动在界面中反映更新后的字段,无需额外代码。
与文档缓存(按查询键缓存)相比,这种自动传播是规范化缓存的主要优势。
cache.readQuery
读取查询当前的缓存结果:cache.readQuery({ query: GET_USERS }) 会返回数据对象,就像 useQuery 返回了该对象一样。如果查询不在缓存中,则返回 null。
请在变更更新函数中使用 readQuery,在修改列表前读取当前列表。
cache.writeQuery
cache.writeQuery({ query: GET_USERS, data: { users: updatedUsers } }) 会直接写入缓存,从而触发所有读取 GET_USERS 的组件重新渲染。此操作不会发起网络请求。
请结合使用 readQuery 和 writeQuery 来实现不可变的缓存更新:读取数据、生成新数组,再写回缓存。
使用 cache.modify 直接更新实体
cache.modify({ id: cache.identify(user), fields: { name: () => 'New Name' } }) 会直接修改特定缓存实体的字段。当您知道实体的缓存 ID 时,无需先读取查询。
fields 对象会将字段名称映射到修改函数;这些函数接收当前值并返回新值。
变更后更新缓存
将更新函数传递给 useMutation:useMutation(ADD_POST, { update(cache, { data: { addPost } }) { cache.modify({ id: cache.identify(user), fields: { posts: existingPosts => [...existingPosts, addPost] } }); } })。
此操作会将新文章追加到用户的缓存文章数组中,并更新所有显示用户文章列表的组件。
cache.evict:移除缓存条目
cache.evict({ id: 'User:1' }) 会从缓存中移除 User:1 条目。任何包含 User:1 的活动查询都会重新渲染,结果中不再包含该实体。
移除条目后,请调用 cache.gc(),删除当前已无法从根查询访问的对象。这可以防止长时间运行的应用出现内存泄漏。
垃圾回收
cache.gc() 会从所有活动查询开始遍历缓存图,并移除不再可访问的实体。您可以定期调用它,也可以在批量变更删除大量实体后调用。
活动 useQuery 钩子引用的实体永远不会被垃圾回收;只有不再属于任何查询结果的孤立实体才会被回收。
使用字段策略实现缓存重定向
如果您查询的单个实体(按 ID 查询 GET_USER)已经作为列表查询的一部分被缓存,Apollo 可以通过字段策略直接从缓存读取,无需往返网络。这些策略包括类型策略中的 keyArgs 和读取函数。
读取函数会返回缓存引用:return toReference({ __typename: 'User', id: args.id }),告诉 Apollo 从现有的 User:id 缓存条目中读取。
为非标准 ID 自定义 keyFields
如果实体使用 id 以外的字段作为唯一键(例如 slug 或 uuid),请在类型策略中配置该字段:new InMemoryCache({ typePolicies: { Post: { keyFields: ['slug'] } } })。
这样,Apollo 会使用 Post:my-post-slug 作为缓存键,而不要求实体包含 id 字段,从而为非标准架构保留规范化能力。
refetchQueries 与更新函数
在 useMutation 选项中配置 refetchQueries: [{ query: GET_USERS }],会在变更完成后触发网络重新获取。它更简单,但每次都会发起网络请求。
更新函数会在本地修改缓存,避免网络往返。当缓存更新逻辑过于复杂,或者服务器计算的字段使本地缓存更新不可靠时,请使用 refetchQueries。
InMemoryCache 的规范化缓存键
InMemoryCache 存储实体时使用的默认缓存键格式是什么?
课程回顾
InMemoryCache 通过 __typename+id 规范化实体,从而实现不同查询之间的自动更新。请使用 cache.readQuery、cache.writeQuery 和 cache.modify 读写缓存。请使用 cache.evict 移除实体,然后调用 cache.gc。对于非 id 主键,请通过 typePolicies 配置自定义键字段。
请使用变更更新函数提高本地缓存效率;当服务器计算的字段使本地更新不可靠时,请使用 refetchQueries。
常见问题解答
「Apollo 缓存:规范化与更新」课时是免费的吗?
是的 — 「Apollo 缓存:规范化与更新」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 React Academy 课程的其余内容,请升级到 CoddyKit PRO。 React Academy 课程共包含 4 节课。
「Apollo 缓存:规范化与更新」这节课中我会学到什么?
了解 Apollo 的规范化 InMemoryCache,并在变更后更新缓存数据而无需重新获取 你通过在浏览器中直接运行的动手代码来练习 React Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 React Academy 需要有经验吗?
无需任何先前经验。CoddyKit 上的 React Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 4 节课,共 4 节。
「Apollo 缓存:规范化与更新」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 React Academy 课中编写并运行代码吗?
能。每节 React Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。