问题描述
我对如何配合阿波罗客户端缓存以及我们组织查询的特定方式存在一些疑问。
因此,组织查询的方式是将查询分组在同一名称空间下的同一资源上。例如。我们有两个资源users
和accounts
,我们的查询如下所示。
type Query {
user: UserQueries
account: AccountQueries
}
type UserQueries {
all: [User!]!
byId(id: ID!): User
}
type AccountQueries {
all: [Account!]!
byId(id: ID!): Account
}
type User {...}
type Account {...}
由于两种Queries
都没有提供ID字段,因此默认情况下UserQueries
下第二个查询的响应将替换缓存中第一个查询的数据,即首先我创建一个{{1 }}查询,然后取回一些存储在all
字段下的缓存中的数据。然后,如果我进行user
查询,则byId
下缓存中的数据将被新数据替换。
有关此过程的一些问题:
- 我注意到,即使将
user
数据替换为all
数据之后,以前缓存的用户(规范化用户)在缓存中仍然可用,因为我认为这些用户会被垃圾回收因为引用丢失了。垃圾收集器是否不立即进行垃圾收集,而是以一定的节奏运行?还是我完全误解了gc的工作原理? - 除了为查询类型生成唯一ID
byId
之外,还有其他方法可以保留两个响应吗? - 像这样的名称空间查询甚至是个好主意吗?
谢谢大家!
解决方法
如您所知,由于缓存不知道该查询在技术上总是返回相同的对象,因此结果将被其他查询覆盖。
对此有两种解决方案:
您已经确定了第一个对象:为这些对象提供一个静态ID字段。这很丑。第二个方法是为docs中讨论的这些查询字段编写merge
函数。我认为名称空间通常并不是一个坏主意,但是正如您所看到的,您必须为架构中的 any 实体类型编写此合并函数。因此,我建议您谨慎使用命名空间。
垃圾收集器在Apollo 3中也进行了一些更新。现在可以对其进行自定义并随时运行。您可以阅读有关GC here的更多信息。不再引用的对象可能会在缓存中保留一段时间。文档似乎没有指定GC的运行时间。可以通过调用client.gc()
来手动运行它。
实际上,在Apollo 3中使用命名空间查询的正确方法是将keyFields
设置为空数组。
当您这样做时,您会向Apollo显示没有要合并的ID,因此所有内容都应合并在同一缓存键中。
您不必为此编写合并功能。
通过typePolicies
配置中的InMemoryCache
选项进行配置
const apolloClient = new ApolloClient({
cache: new InMemoryCache({
typePolicies: {
UserQueries: {
keyFields: [],},AccountQueries: {
keyFields: [],}),});