Apollo Client的InMemoryCache merge函数仅返回ref属性如何处理
问题原因
Apollo Client 的 InMemoryCache 会默认对所有携带__typename和id/_id字段的对象做规范化存储:所有实体对象会单独存在缓存根节点,对应查询字段下仅存储指向实体的ref引用,所以你在merge函数中直接打印incoming拿到的就是ref数组而非原始数据,这是正常的缓存机制。
解决方案
方案1:保留规范化,直接合并ref数组(推荐)
你不需要在merge函数中手动解析ref,Apollo Client 会在将数据返回给useQuery前自动把所有ref解析为完整的实体数据,仅需补充两个配置即可实现分页列表合并:
- 配置
keyArgs排除分页参数,确保不同页的查询命中同一个缓存字段 - 给
existing加默认值避免首次查询解构报错
import { ApolloClient, InMemoryCache, createHttpLink } from '@apollo/client'; const httpLink = createHttpLink({ uri: '你的GraphQL接口地址' }); const client = new ApolloClient({ ssrMode: typeof window === 'undefined', link: httpLink, cache: new InMemoryCache({ typePolicies: { Query: { fields: { todos: { // 如果你的查询有非分页的过滤参数需要作为缓存键,就写成数组,比如['filter', 'status'],把page、limit等分页参数排除即可 keyArgs: false, merge(existing = [], incoming) { // 直接合并ref数组即可,组件拿到的会是完整的实际数据 return [...existing, ...incoming]; } } } } } }) });
方案2:在merge函数中读取实际数据做业务处理
如果需要在merge阶段对数据做去重、排序等自定义操作,可以用merge函数上下文提供的工具方法解析ref:
todos: { keyArgs: false, merge(existing = [], incoming, { readField, isReference }) { // 解析ref为实际数据的工具函数 const getRealItem = (item) => { if (isReference(item)) { return { id: readField('id', item), content: readField('content', item), completed: readField('completed', item), // 补充你需要的其他Todo字段 } } return item; } const existingList = existing.map(getRealItem); const incomingList = incoming.map(getRealItem); // 示例:合并时按id去重 const uniqueIds = new Set(existingList.map(item => item.id)); const newItems = incomingList.filter(item => !uniqueIds.has(item.id)); return [...existingList, ...newItems]; } }
方案3:关闭Todo类型规范化,直接存储原始数据
如果你的Todo列表不需要单独缓存实体,可以直接关闭该类型的规范化,merge函数中拿到的就是原始数据:
const cache = new InMemoryCache({ typePolicies: { Todo: { // 关闭该类型的规范化存储 keyFields: false }, Query: { fields: { todos: { keyArgs: false, merge(existing = [], incoming) { return [...existing, ...incoming] } } } } } })
注意事项
- 如果使用偏移分页或者Relay风格的游标分页,可以直接使用
@apollo/client/utilities提供的offsetLimitPagination、relayStylePagination工具函数,无需手动编写merge逻辑。 - 必须正确配置
keyArgs,如果分页参数被包含在keyArgs中,不同页的查询会被识别为独立缓存字段,不会触发merge逻辑。
内容的提问来源于stack exchange,提问作者Roosevelt Jointer
相关产品推荐
相关产品推荐

