Apollo Client 中的查询是否应该在发出网络请求之前查找不同查询缓存的结果?
Should a query in Apollo Client look for the results cached by different queries before making a network request?
我正在尝试弄清楚 Apollo Client 中的查询应该如何与缓存交互。
具体来说,我想知道我们是否 运行 一个获取所有待办事项的查询:
todos {
title
completed
}
然后我们 运行 一个查询,该查询获取一个已被 todos 查询获取的待办事项并请求完全相同的字段:
todo(id: $id) {
title
completed
}
第二个查询应该 a) 从缓存中获取数据,还是 b) 发出网络请求?
我的假设是情况 A。这是基于 Apollo 官方博客 post 中的引述:
https://www.apollographql.com/blog/demystifying-cache-normalization/
For example, if we were to:
- Perform a GetAllTodos query, normalizing and caching all todos from a backend
- Call GetTodoById on a todo that we had already retrieved with GetAllTodos
...then Apollo Client could just reach into the cache and get the object directly without making another request.
但是,在我的应用程序中,我不断遇到情况 B,它总是发出额外的网络请求,即使我已经在不同的查询中请求了所有数据。
我假设我做错了什么,所以我查看了这个 Apollo 全栈教程回购 (https://github.com/apollographql/fullstack-tutorial) 并更新了 LaunchDetails 查询以仅请求已在 GetLaunchList 中请求的相同数据询问。这复制了我上面用待办事项详述的相同场景。
查询现在看起来像这样:
export const GET_LAUNCHES = gql`
query GetLaunchList($after: String) {
launches(after: $after) {
cursor
hasMore
launches {
...LaunchTile
}
}
}
${LAUNCH_TILE_DATA}
`;
export const GET_LAUNCH_DETAILS = gql`
query LaunchDetails($launchId: ID!) {
launch(id: $launchId) {
...LaunchTile
}
}
${LAUNCH_TILE_DATA}
`;
我 运行 应用程序,发现为 LaunchDetails 查询发出了一个新的网络请求,即使在 GetLaunchList 查询 运行 之后所有需要的数据已经在缓存中。
我无法在文档中找到任何答案,我从示例教程应用程序中看到的结果似乎与上面博客文章中的引用不一致。
是否只有在查询已经 运行 之前查询才会查看缓存?如果该数据是由不同的查询缓存的,它不能获取缓存的数据吗?我错过了什么吗?
查询将进行网络查询。
todo(id: $id) {
title
completed
}
Apollo 缓存不是很智能。它只是存储。您需要read/write手动进行更复杂的操作。
这是因为 Apollo 不知道您的架构和数据结构。它不知道 todo(id: $id)
将进行 DB 搜索,因此无法优化以在缓存中查找。
如果您不想第二次获取,则必须使用片段实现您的数据获取结构:
try {
return client.readFragment({
id: 'Todo:5', // The value of the to-do item's unique identifier
fragment: gql`
fragment TodoFragment on Todo {
id
title
completed
}
`,
});
} catch(_e) { // if no fragment is found there will be an error
client.query(QUERY, variables: { id: 5})
}
Apollo缓存的方式是,如果你做两个查询:
- 加载待办事项
todos {
id
title
completed
}
- 加载单个待办事项
todo(id: $id) {
id
title
completed
}
如果您列出待办事项列表并加载第二个 - 它将更新待办事项数据。
请在这里看到更好的(在我看来)答案:
直接从该答案复制,感谢作者:
存在此功能,但如果您不知道要查找什么,则很难找到。在 Apollo Client v2 中,您正在寻找缓存重定向功能,在 Apollo Client v3 中,这被替换为类型策略/字段读取策略 (v3 docs)。
Apollo 没有 'know' 您的 GraphQL 模式,这使得在日常使用中设置和使用变得容易。然而,这意味着给定一些查询(例如 getBooks
)它不知道结果类型是什么预先。只要 __typename 启用,它之后就知道了。这是默认行为,是规范化缓存所必需的。
假设您有一个 getBooks
查询,它获取 Book
的列表。如果您在使用 Apollo devtools 完成此请求后检查缓存,您应该使用 Book:123
键在缓存中找到书籍,其中 Book
是类型名称,123
是 ID。如果它存在(并且被查询!),id
字段将用作缓存的标识符。如果你的id字段有其他名字,你可以使用缓存的typePolicies通知Apollo InMemoryCache这个字段。
如果您设置了它,然后 运行 一个 getBook
查询,使用一些 id 作为输入,您将 不会 得到任何缓存数据。原因如前所述:Apollo 并不知道这个查询将要return.
的类型。
因此在 Apollo v2 中,您将使用 cacheRedirect 到 'redirect' Apollo 到正确的缓存:
cacheRedirects: {
Query: {
getBook(_, args, { getCacheKey }) {
return getCacheKey({
__typename: 'Book',
id: args.id,
});
}
},
},
(如果您在 typePolicy 中指定了另一个键,args.id
应替换为另一个标识符)
使用 Apollo v3 时,需要类型策略/字段读取策略:
typePolicies: {
Query: {
fields: {
getBook(_, { args, toReference }) {
return toReference({
__typename: 'Book',
id: args.id,
});
}
}
}
}
我正在尝试弄清楚 Apollo Client 中的查询应该如何与缓存交互。
具体来说,我想知道我们是否 运行 一个获取所有待办事项的查询:
todos {
title
completed
}
然后我们 运行 一个查询,该查询获取一个已被 todos 查询获取的待办事项并请求完全相同的字段:
todo(id: $id) {
title
completed
}
第二个查询应该 a) 从缓存中获取数据,还是 b) 发出网络请求?
我的假设是情况 A。这是基于 Apollo 官方博客 post 中的引述:
https://www.apollographql.com/blog/demystifying-cache-normalization/
For example, if we were to:
- Perform a GetAllTodos query, normalizing and caching all todos from a backend
- Call GetTodoById on a todo that we had already retrieved with GetAllTodos
...then Apollo Client could just reach into the cache and get the object directly without making another request.
但是,在我的应用程序中,我不断遇到情况 B,它总是发出额外的网络请求,即使我已经在不同的查询中请求了所有数据。
我假设我做错了什么,所以我查看了这个 Apollo 全栈教程回购 (https://github.com/apollographql/fullstack-tutorial) 并更新了 LaunchDetails 查询以仅请求已在 GetLaunchList 中请求的相同数据询问。这复制了我上面用待办事项详述的相同场景。
查询现在看起来像这样:
export const GET_LAUNCHES = gql`
query GetLaunchList($after: String) {
launches(after: $after) {
cursor
hasMore
launches {
...LaunchTile
}
}
}
${LAUNCH_TILE_DATA}
`;
export const GET_LAUNCH_DETAILS = gql`
query LaunchDetails($launchId: ID!) {
launch(id: $launchId) {
...LaunchTile
}
}
${LAUNCH_TILE_DATA}
`;
我 运行 应用程序,发现为 LaunchDetails 查询发出了一个新的网络请求,即使在 GetLaunchList 查询 运行 之后所有需要的数据已经在缓存中。
我无法在文档中找到任何答案,我从示例教程应用程序中看到的结果似乎与上面博客文章中的引用不一致。
是否只有在查询已经 运行 之前查询才会查看缓存?如果该数据是由不同的查询缓存的,它不能获取缓存的数据吗?我错过了什么吗?
查询将进行网络查询。
todo(id: $id) {
title
completed
}
Apollo 缓存不是很智能。它只是存储。您需要read/write手动进行更复杂的操作。
这是因为 Apollo 不知道您的架构和数据结构。它不知道 todo(id: $id)
将进行 DB 搜索,因此无法优化以在缓存中查找。
如果您不想第二次获取,则必须使用片段实现您的数据获取结构:
try {
return client.readFragment({
id: 'Todo:5', // The value of the to-do item's unique identifier
fragment: gql`
fragment TodoFragment on Todo {
id
title
completed
}
`,
});
} catch(_e) { // if no fragment is found there will be an error
client.query(QUERY, variables: { id: 5})
}
Apollo缓存的方式是,如果你做两个查询:
- 加载待办事项
todos {
id
title
completed
}
- 加载单个待办事项
todo(id: $id) {
id
title
completed
}
如果您列出待办事项列表并加载第二个 - 它将更新待办事项数据。
请在这里看到更好的(在我看来)答案:
直接从该答案复制,感谢作者:
存在此功能,但如果您不知道要查找什么,则很难找到。在 Apollo Client v2 中,您正在寻找缓存重定向功能,在 Apollo Client v3 中,这被替换为类型策略/字段读取策略 (v3 docs)。
Apollo 没有 'know' 您的 GraphQL 模式,这使得在日常使用中设置和使用变得容易。然而,这意味着给定一些查询(例如 getBooks
)它不知道结果类型是什么预先。只要 __typename 启用,它之后就知道了。这是默认行为,是规范化缓存所必需的。
假设您有一个 getBooks
查询,它获取 Book
的列表。如果您在使用 Apollo devtools 完成此请求后检查缓存,您应该使用 Book:123
键在缓存中找到书籍,其中 Book
是类型名称,123
是 ID。如果它存在(并且被查询!),id
字段将用作缓存的标识符。如果你的id字段有其他名字,你可以使用缓存的typePolicies通知Apollo InMemoryCache这个字段。
如果您设置了它,然后 运行 一个 getBook
查询,使用一些 id 作为输入,您将 不会 得到任何缓存数据。原因如前所述:Apollo 并不知道这个查询将要return.
因此在 Apollo v2 中,您将使用 cacheRedirect 到 'redirect' Apollo 到正确的缓存:
cacheRedirects: {
Query: {
getBook(_, args, { getCacheKey }) {
return getCacheKey({
__typename: 'Book',
id: args.id,
});
}
},
},
(如果您在 typePolicy 中指定了另一个键,args.id
应替换为另一个标识符)
使用 Apollo v3 时,需要类型策略/字段读取策略:
typePolicies: {
Query: {
fields: {
getBook(_, { args, toReference }) {
return toReference({
__typename: 'Book',
id: args.id,
});
}
}
}
}