GraphQL缓存机制解析:打破缓存误区
您可能听说过诸如“GraphQL不支持缓存”或“GraphQL不关心缓存”之类的说法。对于许多人来说,这是一个大问题。但事实并非如此。GraphQL官方文档中提到了多种缓存技术,可见其开发团队非常重视缓存及其性能优势。
本文旨在澄清GraphQL与缓存之间的关系,并介绍不同的缓存技术以及如何利用缓存优化GraphQL查询。
考虑以下用于获取帖子及其作者的查询:
query getPost { post(slug: "working-with-graphql-caching") { id title author { id name avatar } } }
实现GraphQL自动缓存的“秘诀”是__typename
元字段,所有GraphQL API都提供此字段。顾名思义,__typename
返回对象的类型名称。此字段甚至可以手动添加到现有查询中,大多数GraphQL客户端或CDN会自动添加它,例如urql。服务器接收到的查询可能如下所示:
query getPost { post(slug: "working-with-graphql-caching") { __typename id title author { __typename id name } } }
包含__typename
的响应可能如下所示:
{ data: { __typename: "Post", id: 5, title: "Working with GraphQL Caching", author: { __typename: "User", id: 1, name: "Jamie Barton" } } }
__typename
是GraphQL缓存的关键,因为它允许我们缓存结果,并知道它包含Post ID 5和User ID 1。
Apollo和Relay等库也提供一定程度的内置缓存,用于自动缓存。由于它们已经知道缓存中的内容,因此它们可以利用缓存而不是远程API来获取客户端在查询中请求的内容。
假设帖子作者使用editPost
变异修改了帖子的标题:
mutation { editPost(input: { id: 5, title: "Working with GraphQL Caching" }) { id title } }
由于GraphQL客户端会自动添加__typename
字段,因此此变异的结果会立即告诉缓存Post ID 5已更改,并且包含该帖子的任何缓存查询结果都需要失效:
{ data: { __typename: "Post", id: 5, title: "Working with GraphQL Caching" } }
下次用户发送相同的查询时,查询将从源获取新数据,而不是使用缓存中的过期结果。
许多GraphQL客户端不会缓存整个查询结果。相反,它们会将缓存数据规范化为两种数据结构:一种将每个对象与其数据关联起来(例如,Post #5:{…},User #1:{…}等);另一种将每个查询与其包含的对象关联起来(例如,getPost:{Post #5,User #1}等)。
请参阅urql关于规范化缓存的文档或Apollo的“Demystifying Cache Normalization”以了解具体的示例和用例。
GraphQL缓存无法自动处理的一个主要边缘情况是向列表中添加项目。因此,如果createPost
变异通过缓存,它不知道要将该项目添加到哪个特定列表中。
最简单的“解决方法”是在变异中查询父类型(如果存在)。例如,在下面的查询中,我们查询了post上的community关系:
query getPost { post(slug: "working-with-graphql-caching") { id title author { id name avatar } # Also query the community of the post community { id name } } }
然后我们也可以从createPost
变异中查询该community,并使包含该community的任何缓存查询结果失效:
mutation createPost { createPost(input: { ... }) { id title # Also query and thus invalidate the community of the post community { id name } } }
虽然并不完美,但类型化模式和__typename
元字段是使GraphQL API非常适合缓存的关键。
您可能认为所有这些都是权宜之计,GraphQL仍然不支持传统的缓存。您不会错。由于GraphQL通过POST请求运行,您需要卸载到应用程序客户端并使用上述“技巧”来利用GraphQL的现代浏览器缓存。
但是,这种方法并不总是可行的,也不是管理客户端缓存的最佳方法。对于更棘手的情况,GraphQL客户端需要您手动更新缓存,但像GraphCDN这样的服务提供了“类似服务器”的缓存体验,还提供了一个手动清除API,允许您执行以下操作以获得更好的缓存控制:
# Purge all occurrences of a specific object mutation { purgeUser(id: [5]) }
# Purge by query name mutation { _purgeQuery(queries: [listUsers, listPosts]) }
# Purge all occurrences of a type mutation { purgeUser }
现在,无论您在何处使用GraphCDN端点,都不再需要在移动端、Web端等所有客户端逻辑中重新实现缓存策略。边缘缓存使您的API速度非常快,并通过在用户之间共享缓存并将其与每个用户的客户端分开来减少负载。
最近在一个项目中使用了GraphCDN,它帮我处理了客户端或服务器上的缓存配置,让我能够继续我的项目。例如,我可以将我的端点替换为GraphCDN,并免费获得查询复杂性(即将推出)、分析等功能。
那么,GraphQL是否关心缓存?当然关心!它不仅提供了一些内置的自动缓存方法,而且许多GraphQL库提供了其他方法来实现和管理缓存。
希望本文能帮助您了解GraphQL缓存机制,以及如何在客户端实现它,以及如何利用CDN来完成所有工作。我的目标不是说服您在所有项目中都使用GraphQL,但如果您正在选择查询语言并且缓存是一个重要因素,请知道GraphQL完全能够胜任这项任务。
以上是使用GraphQL缓存的详细内容。更多信息请关注PHP中文网其他相关文章!