Apollo文档discusses the use of cacheRedirects
告诉Apollo如何从其他查询访问已经在缓存中的数据。
它给出了一个例子:
在某些情况下,查询以不同的密钥请求客户端存储中已存在的数据。一个非常常见的例子是当您的UI具有列表视图和详细视图时,它们都使用相同的数据。列表视图可能会运行以下查询:
查询ListView {books {id title abstract}}
选择特定书籍后,详细信息视图将使用此查询显示单个项目:
查询DetailView {book(id:$ id){id title abstract}}
我们知道数据很可能已经存在于客户端缓存中,但由于它是通过不同的查询请求的,因此Apollo Client不知道这一点。为了告诉Apollo Client在哪里查找数据,我们可以定义自定义解析器
我试图理解为什么这个例子是必要的。如果books
查询返回Book
类型的数组,并且book
请求返回Book
类型的单个对象,那么基于typename和id,规范化缓存肯定已经拥有每本书(来自ListView查询)的数据,并且DetailView
查询可以直接使用该信息而无需任何进一步的干预。相反,我们被告知写一些代码来帮助它:
const cache = new InMemoryCache({
cacheRedirects: {
Query: {
book: (_, args, { getCacheKey }) =>
getCacheKey({ __typename: 'Book', id: args.id })
},
},
});
ApolloClient究竟在哪种情况下无法解决这个问题,为什么呢?
看起来很明显和直观,如下所示,我们通过它的Book
属性获取单个id
对象。
query DetailView($id: ID) {
book(id: $id) {
id
title
abstract
}
}
但是,在此示例中,此处的参数名称(id
)恰好匹配缓存使用的属性的名称(id
)。在惯例之外,没有任何东西可以说论证本身不能被称为bookId
,bookID
或supercalifragilisticexpialidocious
。即使查询返回Book
类型并且接受一个或多个参数,Apollo也无法推断哪个参数实际上是缓存规范化中使用的id。类似地,如果存在其他参数,则它们可能或者可能不重要,无论是否可以使用当前缓存的内容 - 需要进一步的逻辑来确定它。
这里的另一个考虑因素是,除了可选地将IntrospectionFragmentMatcher
的实例传递给你的InMemoryCache
之外,Apollo实际上并不知道它查询的端点的模式是什么。在使用__typename
属性获取查询后,将确定标准化中缓存使用的类型。 cacheRedirects
的重点是防止在项目或项目已经在缓存中时触发查询。但是,给定一个特定的查询,Apollo无法知道它将返回特定类型,直到该查询返回。 cacheRedirects
提供了一种说法“此查询将返回此特定类型”的方法,而不会首先触发查询。
只是事后的想法:这本质上是由于GraphQL设计。在GraphQL中,book(id)
它的解析器可以任意方式自由地解释id
。它只是一个带有单个参数的函数调用,它恰好返回Book
类型的实例。
GraphQL实际上没有任何关于ID的内容。它只承认__typename
特殊。
只有Relay,在某种程度上Apollo,稍后会添加一个对象ID的概念。他们采用了一种不同的方法,使用Relay强制将GraphQL架构转换为更严格的正式结构。
关于架构不可用于客户端甚至还有额外的复杂性(另一个答案也提到)(因此客户端甚至不知道book
将返回Book
而不进行查询)。这再次遵循GraphQL规范。