从10月8日至10日加入我们在纽约市的活动,了解有关 GraphQL 联邦和 API 平台工程的最新技巧、趋势和新闻。参加在纽约市的 GraphQL Summit 2024
文档
免费开始

客户端缓存


支持客户端缓存 GraphQL 响应数据。利用我们的缓存机制,您的应用可以使用之前已获取的本地缓存数据进行 GraphQL 请求。这有助于减少网络流量,带来以下好处:

  • 更短的加载时间
  • 减少服务器负载和成本
  • 减少您的应用用户的流量消耗

Apollo iOS 使用正常的缓存,当配置正确时,作为您 的准确信息来源,使您的应用能在获取变化时做出反应。

Apollo iOS 库包含一个短暂存在的内存缓存和一个 SQLite 缓存,后者将缓存数据持久化到磁盘。

了解如何使用缓存策略来配置 GraphQL 如何与缓存数据交互,请参阅我们的文档关于 获取本地缓存数据.

什么是标准化缓存?

在一个 中,标准化缓存将您每个 GraphQL 响应分解成包含的各个独立对象。然后,每个对象根据其 缓存键 作为独立条目进行缓存。这意味着如果多个响应包含相同的对象,该对象可以合并为一个缓存条目。这有助于减少缓存整体大小,并有助于保持缓存数据的一致性和新鲜度。

由于标准化缓存在所有操作中更新缓存条目,因此一个操作获取的数据可以更新另一个操作获取的对象。这使您能够 监控查询 并且在整个应用程序中响应当前获取的变化。您可以使用此功能自动更新您的 UI 或在数据准备就绪时触发其他事件。

标准化响应

为了保持缓存规范化, Apollo iOS 处理您的 GraphQL 操作的响应数据,识别每个对象,并为新缓存条目创建或合并数据到现有缓存条目。

为了理解 Apollo iOS 如何这样做,考虑以下示例

查询
query GetFavoriteBook {
favoriteBook { # Book object
id
title
author { # Author object
id
name
}
}
}

favoriteBook 中返回一个Book对象,它反过来又包含一个Author对象。来自 GraphQL 服务器的示例响应可能如下所示:

响应
{
"favoriteBook": {
"id": "bk123",
"title": "Les Guerriers du silence",
"author": {
"id": "au456",
"name": "Pierre Bordage"
}
}
}

标准化缓存不会直接存储这个响应。相反,它会将其拆分成单独的缓存条目。默认情况下,这些缓存条目通过它们从根操作的路径来识别。

缓存条目
"QUERY_ROOT": {
"favoriteBook": "-> #QUERY_ROOT.favoriteBook"
}
"QUERY_ROOT.favoriteBook": {
"id": "bk123",
"title": "Les guerriers du silence",
"author": "-> #QUERY_ROOT.favoriteBook.author"
}
"QUERY_ROOT.favoriteBook.author": {
"id": "au456",
"name": "Pierre Bordage"
}

如果你已经缓存了至少一个查询的结果,那么QUERY_ROOT条目总是存在的。这个条目包含了你在任何查询中包含的每个顶级字段的引用(例如,favoriteBook)。

具有aversauthor字段的favoriteBook条目包含一个字符串"-> #QUERY_ROOT.favoriteBook.author"的引用。其中-> #表示这是一个指向另一个缓存条目的引用,在本例中是QUERY_ROOT.favoriteBook.author条目。

根据响应路径对对象进行标准化允许我们将其他操作沿相同响应路径的改变合并。

例如,如果我们定义了另一个查询,该查询从favoriteBook对象中获取额外的,它们可以合并到现有的缓存条目中。

查询
query FavoriteBookYear {
favoriteBook { # Book object
id
yearPublished
}
}
响应
{
"favoriteBook": {
"id": "bk123",
"yearPublished": 1993
}
}

在将此响应合并到缓存后,favoriteBook条目会添加包含现有数据的yearPublished字段。

缓存条目
"QUERY_ROOT.favoriteBook": {
"id": "bk123",
"title": "Les guerriers du silence",
"author": "-> #QUERY_ROOT.favoriteBook.author",
"yearPublished": 1993
}

现在favoriteBook字段可以查询其titleyearPublished在一个新的query中,并且标准化的缓存可以立即从本地缓存返回响应,无需将查询发送到服务器。

查询
query FavoriteBookTitleAndYear {
favoriteBook { # Book object
title
yearPublished
}
}

根据缓存键标准化对象

本部分解释了如何使用缓存键在标准化缓存中合并对象数据。有关如何配置你的缓存键的信息,请参阅自定义缓存键

通过响应路径对响应数据进行标准化有助于我们去重相同字段的响应,但它不允许我们将来自不同字段且返回相同对象的缓存条目合并。

在此query中,我们使用路径为bestFriend.favoriteBookfield获取一个Book对象。

查询
query BestFriendsFavoriteBook {
bestFriend {
favoriteBook { # Book object
id
title
genre
}
}
}
响应
{
"bestFriend" {
"favoriteBook": {
"id": "bk123",
"title": "Les guerriers du silence",
"genre": "SCIENCE_FICTION"
}
}
}

当这个响应合并到缓存中时,我们为 QUERY_ROOT.bestFriendQUERY_ROOT.bestFriend.favoriteBook 添加了新的缓存条目。

响应告诉我们,我们的 bestFriend 和我们有相同的 favoriteBook!然而,同一本书的数据在我们的缓存条目中没有被去重。

缓存条目
"QUERY_ROOT.favoriteBook": {
"id": "bk123",
"title": "Les guerriers du silence",
"author": "-> #QUERY_ROOT.favoriteBook.author",
"yearPublished": 1993
}
"QUERY_ROOT.bestFriend": {
"favoriteBook": "-> #QUERY_ROOT.bestFriend.favoriteBook"
}
"QUERY_ROOT.bestFriend.favoriteBook": {
"id": "bk123",
"title": "Les guerriers du silence",
"genre": "SCIENCE_FICTION"
}

如果我们尝试获取具有 字段 favoriteBook.genre 的查询,缓存将找不到缓存条目 QUERY_ROOT.favoriteBook 上的 字段,因此它会将查询发送到服务器以获取重复数据。

为了去重不同 返回的相同对象的数据,我们需要配置缓存以便它能够识别它们是相同的对象。我们可以通过为 Book 对象提供缓存键配置来实现这一点。

在这个示例中,Book 对象类型有一个 id 字段,该字段可以唯一地标识它。由于我们的 favoriteBookbestFriend.favoriteBook 缓存条目具有相同的 id,我们知道它们代表相同的 Book 对象。我们可以配置缓存,使用 id 字段作为所有 Book 对象的缓存 ID。这将确保缓存正确地标准化我们的缓存条目。

要配置缓存键,我们从 SchemaConfiguration.cacheKeyInfo(for type:,object:) 函数返回一个新的 CacheKeyInfo 值。

SchemaConfiguration.swift
static func cacheKeyInfo(for type: Object, object: JSONObject) -> CacheKeyInfo? {
switch type {
case MySchema.Objects.Book:
return try? CacheKeyInfo(jsonValue: object["id"])
default: return nil
}
}

使用此设置,每次正常化的缓存为 Book 对象写入响应数据时,它将使用 id 来构建缓存键,而不是响应路径。

为了防止不同的 对象类型 之间的缓存键冲突,缓存会在提供的缓存 ID 前面加上对象的 __typename,后面跟着冒号 (:)。

这意味着我们的 Book 的缓存键现在是 "Book:bk123"

有关使用 CacheKeyInfo 配置缓存键的更多信息,请参阅 自定义缓存键

配置 Book 类型的缓存键解析后,上述查询的响应数据将创建一个单一的、规范化的 Book 对象。

缓存条目
"QUERY_ROOT": {
"favoriteBook": "-> #Book:bk123"
}
"BOOK:bk123": {
"id": "bk123",
"title": "Les guerriers du silence",
"author": "-> #QUERY_ROOT.favoriteBook.author",
"yearPublished": 1993,
"genre": "SCIENCE_FICTION"
}
"QUERY_ROOT.bestFriend": {
"favoriteBook": "-> #Book:bk123"
}

缓存项 BOOK:bk123 包含了从所有查询中从 字段 获取的所有 Book

此外,favoriteBookbestFriend.favoriteBook 字段 是指向具有缓存键 BOOK:bk123 的条目的缓存引用。

有关更多信息关于 过程,请参阅我们的博客文章:

清除缓存数据

可以调用 clear(callbackQueue:completion:) 在您的 ApolloStore 中彻底清除所有缓存。

如果您需要更直接地与缓存交互,请查看 直接缓存访问

上一页
@defer 支持(实验性)
下一页
设置
评分文章评分在GitHub上编辑编辑论坛Discord

©2024Apollo Graph Inc.,商业名称为 Apollo GraphQL。

隐私政策

公司