垃圾回收和缓存驱逐
Apollo 客户端 3 允许您选择移除不再有用的缓存数据。默认垃圾回收策略的 gc
方法适用于大多数应用程序,但 evict
方法为需要更精细控制的提供了更多的控制。
您可以直接在 InMemoryCache
对象上调用这些方法,而不是在 ApolloClient
对象上。
cache.gc()
该方法从标准化的缓存中移除所有不可达的对象:
cache.gc();
为了确定一个对象是否可行,缓存从所有已知的根对象(通常为Query
和/或Mutation
)开始,并使用跟踪策略递归访问所有可用的子引用。在过程中未访问的任何标准化对象将被移除。该方法cache.gc()
返回被移除对象的ID列表。
除了修剪你的GraphQL数据外,cache.gc
还可以释放缓存为保留之前缓存结果中未更改部分而使用的内存:
cache.gc({ resetResultCache: true })
暂时释放此内存会减慢缓存读取,因为这些读取没有从任何以前的读取工作中受益。
如果您正在使用canonizeResults: true
选项,cache.gc
还可以重置用于查找规范结果对象的内存:
cache.gc({resetResultCache: true,resetResultIdentities: true,})
⚠️废弃警告对于`canonizeResults`:canonizeResults
的引入可能会导致内存泄漏,所以我们通常不再推荐使用此选项。Apollo Client的将来版本将包含类似的功能,且无内存泄漏的风险。
这些额外的cache.gc
选项对于调查内存使用模式或泄漏非常有用。在获取堆快照或记录分配时间线之前,使用浏览器的开发工具强制进行JavaScript垃圾回收是一个好主意,以确保缓存释放的内存已完全收集并返回到堆中。
配置垃圾回收
您可以使用retain
方法防止对象(及其子对象)被垃圾回收,即使对象不可达:
cache.retain('my-object-id');
如果您以后想使一个保留的对象进行垃圾回收,请使用release
方法:
cache.release('my-object-id');
如果对象不可达,它将在下一次调用gc
时被垃圾回收。
cache.evict()
您可以使用evict
方法从缓存中移除任何标准化对象:
cache.evict({ id: 'my-object-id' })
您还可以通过提供要删除的字段的名称来从缓存对象中删除一个单个字段:
cache.evict({ id: 'my-object-id', fieldName: 'yearOfFounding' });
删除对象通常会使得其他缓存对象不可达。因此,在从缓存中删除一个或多个对象后,您应调用cache.gc
。
悬挂引用
当一个对象从缓存中删除时,对该对象的引用可能仍然存在于其他缓存对象中。Apollo Client默认保留这些悬挂引用,因为所引用的对象可能在稍后写回缓存。这意味着引用可能仍然有用。
您可以自定义悬挂引用的行为,通过为可能包含一个悬挂引用的任何 read
函数定义自定义函数。此函数可以在字段引用的对象缺失时执行必要的清理工作。例如,read
函数可能:
- 从可用对象列表中过滤出引用对象
- 将字段的值设置为
null
- 返回特定的默认值
每个 read
函数都传入一个 canRead
函数,该函数有助于其检测 field
当前是否包含悬挂引用。
以下代码定义了两个 read
函数(一个用于 Query.ruler
,另一个用于 Deity.offspring
),它们都使用 canRead
:
new InMemoryCache({typePolicies: {Query: {fields: {ruler(existingRuler, { canRead, toReference }) {// If there is no existing ruler, Apollo becomes the ruling deityreturn canRead(existingRuler) ? existingRuler : toReference({__typename: "Deity",name: "Apollo",});},},},Deity: {keyFields: ["name"],fields: {offspring(existingOffspring: Reference[], { canRead }) {// Filter out any dangling references left over from removing// offspring, supplying a default empty array if there are no// offspring left.return existingOffspring? existingOffspring.filter(canRead): [];},},},},})
- 如果现有的
ruler
已被罢黜,则Query.ruler
的read
函数将返回默认的统治者(Apollo
)。 - 如果现有的
ruler
已被罢黜,则Query.ruler
的read
函数将返回默认的统治者(Apollo
)。
从缓存列表字段中过滤出悬挂引用(如上面的 Deity.offspring
示例所示)非常常见,因此列字段默认的 read
函数会自动执行此过滤。您可以定义一个自定义 read
函数来覆盖此行为。
对于包含单个悬挂引用的字段(如上面的 Query.ruler
示例所示),没有类似常见的解决方案,因此编写自定义 read
函数最常用到此。