You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Apollo GraphQL缓存问题:如何分离分类列表与详情缓存

解决Apollo GraphQL列表与详情缓存冲突的问题

核心原因

Apollo Client默认会对返回对象按id进行数据归一化存储,同一个id的对象会被合并更新。所以当你拉取category详情(包含所有items)时,会覆盖缓存中该category的items字段,导致列表查询读取到混入不可用items的更新数据。

实用解决方案

方案1:用字段别名分离不同的items集合

修改GraphQL查询,为列表和详情的items字段设置不同别名,让Apollo将它们作为独立字段存储在Category对象中,避免互相覆盖。

列表查询(仅取可用items):

query GetCategories {
  categories {
    id
    name
    # 给items字段起别名,标记为可用项集合
    availableItems: items(onlyAvailable: true) {
      id
      name
      isAvailable
    }
  }
}

详情查询(取所有items):

query GetCategoryDetails($categoryId: ID!) {
  category(id: $categoryId) {
    id
    name
    # 给items字段起别名,标记为全部项集合
    allItems: items {
      id
      name
      isAvailable
    }
  }
}

之后组件渲染列表时读取availableItems,渲染详情时读取allItems,两个字段的缓存完全独立,不会互相干扰。

方案2:通过Type Policies自定义字段读取逻辑

如果不想修改查询结构,可利用Apollo Client的Type Policies,为Category的items字段定义读取规则,根据查询参数自动过滤数据。

在Apollo Client初始化时配置缓存规则:

import { ApolloClient, InMemoryCache, gql } from '@apollo/client';

const client = new ApolloClient({
  uri: '你的GraphQL端点',
  cache: new InMemoryCache({
    typePolicies: {
      Category: {
        fields: {
          items: {
            read(existingItems, { args, cache }) {
              // 如果查询参数包含onlyAvailable: true,过滤出可用的items
              if (args?.onlyAvailable) {
                return existingItems?.filter(itemRef => {
                  // 从缓存中读取item的isAvailable状态
                  const itemData = cache.readFragment({
                    id: itemRef.__ref,
                    fragment: gql`
                      fragment ItemAvailability on Item {
                        isAvailable
                      }
                    `,
                  });
                  return itemData?.isAvailable === true;
                });
              }
              // 无过滤参数时返回所有items
              return existingItems;
            },
          },
        },
      },
    },
  }),
});

这样,用onlyAvailable: true参数查询列表时,Apollo会自动从缓存的所有items中过滤出可用项;详情查询不带该参数则返回全部items,两者互不影响。

方案3:避免详情查询更新列表缓存(不推荐)

如果不需要缓存详情的items数据,可在详情查询时使用fetchPolicy: 'network-only',并通过cache.writeQuery手动写入非items字段,但这种方法会失去缓存优势,仅适合特殊场景:

const { data } = await client.query({
  query: GET_CATEGORY_DETAILS,
  variables: { categoryId },
  fetchPolicy: 'network-only',
});

// 只写入category的非items字段,避免覆盖列表中的items
client.cache.writeQuery({
  query: GET_CATEGORY_DETAILS,
  variables: { categoryId },
  data: {
    category: {
      ...data.category,
      items: [], // 或者不写入items字段
    },
  },
});

内容的提问来源于stack exchange,提问作者Michał J. Gąsior

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.30 22:09:24