阿波罗客户端缓存并不像我预期的那样工作[英] Apollo client cache doesn't work as I excpected

本文是小编为大家收集整理的关于阿波罗客户端缓存并不像我预期的那样工作的处理/解决方法,可以参考本文帮助大家快速定位并解决问题,中文翻译不准确的可切换到English标签页查看源文。

问题描述

我的问题:

我是GraphQL的新手,我正在使用Apollo服务器和客户端开发我的第一个完整堆栈应用程序,这是一个简单的博客.

在客户端,我在两个不同的页面中使用了相同的查询,但具有不同的变量.查询是根据我正在使用的页面查询ID或SLUG的博客文章.因此结果是相同的,只是查询变量会改变.

当我在一个页面中使用查询时,我认为由于阿波罗缓存,查询不会在第二页上运行.但这不是正在发生的事情.查询再次在第二个页面上运行,当然会返回我与另一页相同的结果.

为什么Apollo在这种情况下不使用缓存?

这是我使用的代码:

在服务器端,我有一个非常基本的查询可以从博客中获取文章,可以通过ID或slug获取:

:

type Query {
  ...
  article(id: ID, slug: String): Article
  ...
}

在客户端,如果文章已发布,我会在Slug上查询一篇文章,或者在仍然是草稿时通过ID进行查询.

slug的查询:

<Query
  query={article}
  variables={{ slug }}
  fetchPolicy="cache-and-network"
>
  {({ loading, error, data }) => {
    return (
      <Article
        loading={loading}
        article={data && data.article}
      />
    );
  }}
</Query>

iD查询是相同的,除了使用ID的变量参数:

<Query
  query={article}
  variables={{ id }}
>
  {({ loading, error, data }) => {
    return (
      <EditArticle loading={loading} article={data && data.article} />
    );
  }}
</Query>

您可以看到,两者都使用相同的GraphQl端点,结果是相同的.但是没有使用缓存.

推荐答案

Apollo假设您的解析器是纯净的(它们没有副作用,并且在相同的输入/参数的情况下,将相同的结果恢复相同的结果).这已经有很多要假设了.想象一下,在新闻网站上返回随机号码或最新评论的解析器.给定相同的输入,两者都不会总是返回相同的结果.另一方面,阿波罗并没有做出有关解决方案的实现的假设.虽然在您的脑海中,您的文章解析器的实现是显而易见的(如果id是id,则带有该ID的返回文章,如果slug是slug,则带有该slug的返回文章),这是从计算机程序中询问的很多东西来猜测.

我已经回答了 .要防止第二个查询运行,您必须实现缓存重定向.不利的一面是,您必须将缓存重定向在客户端上的客户端和解析器上同步.

其他推荐答案

我遇到了同样的问题.从本质上讲,我希望缓存查找仅在尝试使用" slug"查找时简单地失败,而我对此很好,但是它无法生成正确的搜索结果,并且" null"结果返回为查询响应似乎是成功的查询响应.糟糕.

为了避免副作用,我将使用一个单独的GraphQl查询,该查询接受SLUG而不是ID.这还有其他几个好处,例如,我可以在各自的查询中执行"必需"字段.主要的事情是,它使基于ID的查询更加确定性,因此与缓存更加兼容.

type Query {
  ...
  article(id: ID!): Article
  articleBySlug(slug: String!): Article
  ...
}

甚至更好的是使用您的" slug"值搜索缓存的能力,但在不使用" slug"作为缓存ID的一部分的情况下,这似乎尚未得到支持.

本文地址:https://www.itbaoku.cn/post/1938136.html

问题描述

My problem:

I am pretty new to GraphQL and I am developing my first full stack app using Apollo server and client, which is a simple blog.

On client side, I am using the same query in two different pages, but with different variables. Queries are querying a blog article by ID or slug, depending on the page I am using it. So the result is the same, there is just the queries variables that changes.

When I use a query in one page, I thought query wouldn't run on the second page because of Apollo cache. But it is not what is happening. The query runs again in the second, and of course returns me the same result that in the other page.

Why does Apollo doesn't use the cache in this case?

Here is the code I use :

On server side, I have a pretty basic query to fetch an article from a blog, which can be fetched by ID or Slug:

type Query {
  ...
  article(id: ID, slug: String): Article
  ...
}

On client side, I query an article by slug if the article is published, or by ID when it is still a draft.

The query by slug:

<Query
  query={article}
  variables={{ slug }}
  fetchPolicy="cache-and-network"
>
  {({ loading, error, data }) => {
    return (
      <Article
        loading={loading}
        article={data && data.article}
      />
    );
  }}
</Query>

The query by ID is the same, except the variables param which uses ID:

<Query
  query={article}
  variables={{ id }}
>
  {({ loading, error, data }) => {
    return (
      <EditArticle loading={loading} article={data && data.article} />
    );
  }}
</Query>

As you can see, both are using the same GraphQL endpoint, and the result is the same. But the cache is not used.

推荐答案

Apollo assumes that your resolvers are pure (they don't have side effects and formostly return the same result given the same input/arguments). This is already a lot to assume. Imagine a resolver that returns a random number or the newest comment on a news website. Both would not always return the same result given the same input. On the other hand Apollo does not make - and pretty much cannot make - assumptions about the implementation of your resolver. While in your head the implementation for your article resolver is obvious (if the id is present return article with that id, if slug is present return article with that slug) this is a lot to ask from a computer programm to guess.

I have answered a similar question recently. To prevent the second query from running you have to implement a cache redirect. The downside is that you have to keep your cache redirects on the client and resolvers on the server in sync.

其他推荐答案

I have hit this same problem. In essence, I expected the cache lookup to simply fail when it attempts a look up with the "slug" only, and I was fine with that, but instead it fails to generate a correct search result and a "null" result is return as the query response as though it were a successful query response. Oops.

In order to avoid side-effects, I will just be using a separate graphQL query which accepts a slug instead of an ID. This has a couple other benefits, for instance I can enforce the field as "required" in their respective queries. Main thing is that it makes the ID-based query more deterministic and thus more compatible with caching.

type Query {
  ...
  article(id: ID!): Article
  articleBySlug(slug: String!): Article
  ...
}

Even better would be the ability to search the cache using your "slug" value for a matching result but this doesn't seem to be supported yet without using the "slug" as part of the cache ID itself.