Apollo Client React Native缓存:所有查询/突变都需id/_id字段吗?
Apollo Client缓存规范化与无主键类型的常见问题解答
核心前提:Apollo缓存规范化的逻辑
Apollo Client的缓存规范化仅针对带有唯一标识字段(默认id/_id,可自定义)的类型生效:
- 带主键的类型会被拆解为独立对象,存储在缓存的规范化存储区,所有引用该对象的查询结果都会指向这个唯一实例,避免重复存储,且能自动同步更新。
- 不带主键的类型不会被规范化,会作为完整的查询结果条目存储,以「查询名称+变量组合」作为缓存键,无法被识别为同一类数据的不同实例。
针对你的场景的具体问题解答
1. 无id/_id的Rooms类型能否被正确规范化?
不能。Rooms类型没有主键字段,Apollo不会将其拆解到规范化存储区,只会把getChatRoomsForUser的整个查询结果作为独立缓存条目存储。
2. 每次查询是否会重复添加缓存而非更新?
分两种情况:
- 如果
getChatRoomsForUser没有查询变量(userId通过context获取,查询本身不接收参数):每次查询会直接覆盖之前的缓存条目,不会重复存储。 - 如果后续为分页添加了
page等变量:不同变量组合的查询会生成不同的缓存键,此时会生成多个独立的缓存条目,造成冗余存储,且这些条目不会自动合并更新。
3. 会影响突变的缓存自动更新吗?
会严重影响。以删除房间的突变为例:
- 虽然
Room类型有主键,Apollo能正确删除缓存中的Room对象,但getChatRoomsForUser返回的Rooms条目是独立存储的,Apollo无法识别这个条目里的rooms数组引用了被删除的Room,因此不会自动更新该数组。 - 你必须手动编写
update函数,找到getChatRoomsForUser的缓存条目,从rooms数组中移除对应的Room对象。
4. 若Rooms有id/_id,是否无需手动编写update/merge函数?
不一定,得结合场景:
- 首先需要自定义
dataIdFromObject函数,指定Rooms的主键(比如用关联的userId作为唯一标识,因为Rooms是某个用户的房间列表),让Apollo能规范化存储Rooms对象。 - 对于简单的非分页列表:当
Room被删除时,Apollo会自动更新Rooms对象中的rooms数组(因为数组元素是对规范化Room对象的引用)。 - 对于分页列表:即使
Rooms有主键,Apollo默认也不会自动更新不同分页的列表条目,此时仍需结合@connection指令定义列表的唯一标识,或手动编写update函数来同步分页数据。
5. 此类问题会在其他无主键的查询/突变中加剧吗?
是的,问题会更严重:
- 所有无主键的类型,其缓存都以查询条目形式存储,无法共享或自动同步,导致缓存冗余。
- 任何涉及这类类型的突变,都需要手动处理所有相关的缓存条目,维护成本会随着查询/突变数量增加而急剧上升。
优化建议
- 给
Rooms类型添加逻辑主键(比如userId),并通过dataIdFromObject配置告诉Apollo使用该字段作为唯一标识。 - 对于分页列表,使用
@connection指令定义列表的唯一名称,让Apollo能自动合并不同分页的缓存数据。 - 复杂场景下,优先依赖规范化缓存的自动更新能力,减少手动
update函数的编写。
内容的提问来源于stack exchange,提问作者Matt Volonnino
相关产品推荐
相关产品推荐

