如何使用订阅和 AWS AppSync 高效同步 Apollo 的缓存

How to efficiently sync Apollo's cache using subscriptions and AWS AppSync

我在 Node.js 客户端中使用 aws-appsync 来保存缓存的数据项列表。此缓存必须始终可用,包括未连接到 Internet 时。

当我的 Node 应用程序启动时,它会调用一个查询,该查询 returns 来自 AppSync 数据源的整个项目列表。这是由 Apollo 的缓存存储缓存的,它允许仅使用缓存进行未来查询(使用相同的 GraphQL 查询)。

该应用程序还订阅了能够修改其他客户端列表的突变。当列表中的项目发生更改时,新数据将发送到应用程序。这可以触发重新获取整个列表的原始查询,从而使缓存保持最新。

只有一项发生变化时获取整个列表效率不高。 如何使缓存保持最新,同时最大限度地减少每次更改时必须提取的数据量?

解决方案必须提供单点访问缓存数据。这可以是 GraphQL 查询或直接访问缓存存储。但是,使用多个查询的结果不是一种选择。


Apollo documentation 暗示这应该是可能的:

In some cases, just using [automatic store updates] is not enough for your application ... to update correctly. For example, if you want to add something to a list of objects without refetching the entire list ... Apollo Client cannot update existing queries for you.

它建议的备选方案是重新获取(基本上是我上面描述的)和使用 update 回调来手动更新商店中缓存的查询结果。

Using update gives you full control over the cache, allowing you to make changes to your data model in response to a mutation in any way you like. update is the recommended way of updating the cache after a query.

但是,这里指的是同一客户端所做的更改,而不是使用订阅的客户端之间的同步使用。 update 回调选项似乎不适用于订阅(它提供更新的项目数据)或查询(它可以获取更新的项目数据)。

只要您的订阅包含添加的全部资源,就应该可以通过直接读取和写入缓存来实现。假设我们有一个来自文档的订阅:

const COMMENTS_SUBSCRIPTION = gql`
  subscription onCommentAdded {
    commentAdded {
      id
      content
    }
  }
`;

订阅组件包含一个 onSubscriptionData 道具,因此我们应该能够按照以下方式做一些事情:

<Subscription
  subscription={COMMENTS_SUBSCRIPTION}
  onSubscriptionData={({ client, subscriptionData: { data, error } }) => {
    if (!data) return
    const current = client.readQuery({ query: COMMENTS_QUERY })
    client.writeQuery({
      query: COMMENTS_QUERY,
      data: {
        comments: [...current.comments, data.commentAdded],
      },
    })
  }}
/>

或者,如果您使用普通 JavaScript 而不是 React:

const observable = client.subscribe({ query: COMMENTS_SUBSCRIPTION })
observable.subscribe({
  next: (data) => {
    if (!data) return
    const current = client.readQuery({ query: COMMENTS_QUERY })
    client.writeQuery({
      query: COMMENTS_QUERY,
      data: {
        comments: [...current.comments, data.commentAdded],
      },
    })
  },
  complete: console.log,
  error: console.error
})