在发出网络请求之前,Apollo客户端中的查询是否应查找由不同查询缓存的结果?

问题描述

我正在尝试弄清楚Apollo Client中的查询应如何与缓存交互。

具体来说,我想知道我们是否运行可获取所有待办事项的查询

todos {
  title
  completed
}

然后,我们运行一个查询,该查询获取一个已由todos查询获取的待办事项,并请求完全相同的字段:

todo(id: $id) {
  title
  completed
} 

第二个查询是a)从缓存中获取数据,还是b)发出网络请求?

我的假设是情况A。这是基于阿波罗官方博客文章中的引用:

https://www.apollographql.com/blog/demystifying-cache-normalization/

例如,如果我们要:

  1. 执行GetAllTodos查询,从后端标准化和缓存所有待办事项
  2. 在我们已经使用GetAllTodos检索到的待办事项上调用GetTodobyId

...然后Apollo客户端可以直接进入缓存并直接获取对象,而无需发出其他请求。

但是,在我的应用程序中,我一直遇到情况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}
`;

我运行了该应用程序,发现即使在运行GetLaunchList查询之后所有必需的数据已经在缓存中,launchdetails查询也发出了新的网络请求。

我在文档中找不到任何答案,而从示例教程应用程序中看到的结果似乎与上面博客文章中的引文不符。

是否只有在查询已经运行之前查询才会查询缓存?如果该数据是由其他查询缓存的,它是否无法获取缓存的数据?我想念什么吗?

解决方法

查询将进行网络查询。

todo(id: $id) {
  title
  completed
} 

阿波罗缓存不是很聪明。它只是存储。您需要手动读取/写入更多复杂的操作。

原因是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缓存的方式是,如果您执行两个查询:

  1. 加载待办事项
todos {
  id
  title
  completed
}
  1. 加载单个待办事项
todo(id: $id) {
  id
  title
  completed
} 

如果您列出待办事项列表并加载第二个待办事项-它将更新待办事项数据。

,

请在这里看到更好的答案(在我看来):

https://stackoverflow.com/a/66053242/6423036

直接从那个答案复制,感谢作者:

此功能存在,但如果您不知道要查找什么,则很难找到。在 Apollo Client v2 中,您正在寻找缓存重定向功能,在 Apollo Client v3 中,这被类型策略/字段读取策略 (v3 docs) 取代。

Apollo 不“知道”您的 GraphQL 架构,这使得在日常使用中可以轻松设置和使用。然而,这意味着给定一些查询(例如 getBooks)它不知道结果类型将是什么预先。只要启用了 __typename,它就会知道它之后。这是默认行为,是规范化缓存所必需的。

假设您有一个 getBooks 查询,用于获取 Book 的列表。如果在此请求完成后使用 Apollo devtools 检查缓存,您应该使用 Book:123 键查找缓存中的书籍,其中 Book 是类型名称,123 是 ID。如果它存在(并被查询!)id 字段用作缓存的标识符。如果您的 id 字段有其他名称,您可以使用缓存的 typePolicies 将这个字段通知 Apollo InMemoryCache。

如果您已经设置好并在之后运行 getBook 查询,使用某个 ID 作为输入,您将不会获得任何缓存数据。原因如前所述:Apollo 不知道该查询将返回哪种类型。

因此在 Apollo v2 中,您将使用 cacheRedirect 将 Apollo“重定向”到正确的缓存:

  cacheRedirects: {
    Query: {
      getBook(_,args,{ getCacheKey }) {
        return getCacheKey({
          __typename: 'Book',id: args.id,});
      }
    },},

(如果您在 typePolicy 中指定了另一个键,则应将 args.id 替换为另一个标识符)

当使用 Apollo v3 时,你需要一个 typepolicy / field read policy:

  typePolicies: {
    Query: {
      fields: {
        getBook(_,{ args,toReference }) {
          return toReference({
            __typename: 'Book',});
        }
      }
    }
  }