配置 Apollo 客户端缓存
本文介绍缓存设置和配置。要了解如何与缓存数据交互,请参阅读取和写入缓存数据.
初始化
创建一个InMemoryCache
对象并将其提供给 ApolloClient
构造函数,如下所示:
import { InMemoryCache, ApolloClient } from '@apollo/client';const client = new ApolloClient({// ...other arguments...cache: new InMemoryCache(options)});
The InMemoryCache
构造器接受多种 配置选项.
配置选项
您可以根据需要配置缓存的行为。例如,您可以
- 自定义特定类型的 缓存ID
- 自定义单个字段 的存储和检索
- 为 片段匹配定义多态类型关系
- 定义 pagination 的模式pagination
- 管理客户端 local state
为了自定义缓存行为,您需要在 InMemoryCache
构造函数中提供一个配置对象。此对象支持以下 .css-154yii{-webkit-text-decoration:underline;text-decoration:underline;text-decoration-style:dotted;text-decoration-thickness:1.5px;text-decoration-color:var(--chakra-colors-gray-300);text-underline-offset:0.2em;}.css-154yii:hover,.css-154yii[data-hover]{cursor:help;}.chakra-ui-dark .css-154yii:not([data-theme]),[data-theme=dark] .css-154yii:not([data-theme]),.css-154yii[data-theme=dark]{text-decoration-color:var(--chakra-colors-blue-400);}字段:
名称 类型 | 描述 |
---|---|
| 如果 默认情况下,缓存将使用 默认值是 |
| 如果 默认值是 |
| 保留在内存中的结果对象的限制数量,以提高对缓存的重读速度。 默认值是 |
| 包含此对象以定义你模式定义中类型之间的多态关系。这样做可让你通过接口或联合查找缓存数据。 对象的每个键是一个接口或联合的 有关示例,请参阅 手动定义 |
| 包含此对象以根据类型范围自定义缓存的特定行为。 对象的每个键是与要自定义的 __typename |
| 这是一个函数,它接受一个响应对象并返回一个在商店中规范化数据时使用的唯一标识符。 有关详细信息,请参阅 全局自定义标识符生成。 |
自定义缓存 ID
您可以对InMemoryCache
如何生成您架构中各类型的缓存ID进行自定义(查看默认行为)。如果类型使用一个id
或_id
之外为其唯一的识别符。
要实现这一点,您需要为要自定义的每个类型定义一个TypePolicy
。您将所有缓存typePolicies
指定在提供给InMemoryCache
构造函数的`options`对象中。。
在相关的TypePolicy
对象中包含一个keyFields
字段,如下所示:
const cache = new InMemoryCache({typePolicies: {Product: {// In an inventory management system, products might be identified// by their UPC.keyFields: ["upc"],},Person: {// In a user account system, the combination of a person's name AND email// address might uniquely identify them.keyFields: ["name", "email"],},Book: {// If one of the keyFields is an object with fields of its own, you can// include those nested keyFields by using a nested array of strings:keyFields: ["title", "author", ["name"]],},AllProducts: {// Singleton types that have no identifying field can use an empty// array for their keyFields.keyFields: [],},},});
以下示例展示了具有不同keyFields
的typePolicies
:
Product
类型使用其upc
字段作为其识别字段。Person
类型使用其name
和email
字段的组合。Book
类型将一个子字段作为其缓存ID的一部分。["name"]
项表示数组(author
)中前一个字段的name字段是缓存ID的一部分。此时Book
的author
字段必须是一个包含name
字段的对象,才有效。- 有效的
Book
类型的缓存ID具有以下结构:Book:{"title":"Fahrenheit 451","author":{"name":"Ray Bradbury"}}
AllProducts
类型说明了针对单例类型的一种特殊策略。如果缓存将只包含一个AllProducts
对象,并且该对象没有识别字段,您可以为其keyFields
提供空数组。
如果对象有多个keyFields
,则缓存ID始终按相同的顺序列出这些字段以确保唯一性。
请注意,这些keyFields
字符串始终引用方案中定义的标准字段名称。这意味着不对字段别名敏感。
计算对象的缓存ID
全局定制标识生成
import { defaultDataIdFromObject } from '@apollo/client';const cache = new InMemoryCache({dataIdFromObject(responseObject) {switch (responseObject.__typename) {case 'Product': return `Product:${responseObject.upc}`;case 'Person': return `Person:${responseObject.name}:${responseObject.email}`;default: return defaultDataIdFromObject(responseObject);}}});
此代码的缺点如下
- 它没有任何措施来保护对未定义对象属性的访问。
定制类型策略
const cache = new InMemoryCache({typePolicies: {Person: {fields: {name: {read(name = "UNKNOWN NAME") {return name.toUpperCase();}},},},},});// Add a type policy to the cache.cache.policies.addTypePolicies({Person: {fields: {email: {return email;},},},},});
总体而言,代码为Person
类型设置缓存行为,确保如果缓存中没有这些字段,则name
和email
字段提供默认值。
禁用归一化
您可以指示InMemoryCache
不要对特定类型的对象进行归一化。这对于由时间戳标识且永远不会接收到更新的指标和其他临时数据非常有用。
要禁用类型的归一化,为类型定义一个TypePolicy
(如自定义缓存ID中所示)并设置策略的keyFields
字段为false
未归一化的对象将嵌入其在缓存中的父对象中。您不能直接访问这些对象,但可以通过其父对象访问它们。
TypePolicy
字段
要通过自定义缓存与模式中特定类型交互的方式,可以将映射__typename
字符串到TypePolicy
对象的InMemoryCache
构造函数传递一个对象。
一个TypePolicy
对象可以包含以下字段:
type TypePolicy = {// Allows defining the primary key fields for this type, either using an// array of field names, a function that returns an arbitrary string, or// false to disable normalization for objects of this type.keyFields?: KeySpecifier | KeyFieldsFunction | false;// If your schema uses a custom __typename for any of the root Query,// Mutation, and/or Subscription types (rare), set the corresponding// field below to true to indicate that this type serves as that type.queryType?: true,mutationType?: true,subscriptionType?: true,fields?: {[fieldName: string]:| FieldPolicy<StoreValue>| FieldReadFunction<StoreValue>;}};// Recursive type aliases are coming in TypeScript 3.7, so this isn't the// actual type we use, but it's what it should be:type KeySpecifier = (string | KeySpecifier)[];type KeyFieldsFunction = (object: Readonly<StoreObject>,context: {typename: string;selectionSet?: SelectionSetNode;fragmentMap?: FragmentMap;},) => string | null | void;
重写根操作类型(不常见)
除keyFields
外,一个TypePolicy
还可以通过设置queryType
、mutationType
或subscriptionType
为true
来指示其类型代表根查询、mutation或subscription类型:
const cache = new InMemoryCache({typePolicies: {UnconventionalRootQuery: {// The RootQueryFragment can only match if the cache knows the __typename// of the root query object.queryType: true,},},});const result = cache.readQuery({query: gql`query MyQuery {...RootQueryFragment}fragment RootQueryFragment on UnconventionalRootQuery {field1field2 {subfield}}`,});const equivalentResult = cache.readQuery({query: gql`query MyQuery {field1field2 {subfield}}`,});
缓存通常通过在发送到服务器的每个选择集中添加__typename
字段来获取__typename
信息。技术上,它可以使用相同的方法为每个操作的最外层选择集获取__typename
,但根查询和mutation的__typename
几乎总是"Query"
和"Mutation"
,因此缓存假设这些常用默认值,除非在TypePolicy
中另有说明。
对于图中的大多数对象,图来说,字段__typename
对于正确识别和规范化至关重要。对于根查询和变异类型,__typename
几乎没有什么用或重要,因为这些类型是单个实例,每个客户端只有一个。
fields
属性
在TypePolicy
中,最后一个属性是fields
属性,它允许您定制单个缓存的字段行为。