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
相关产品推荐
相关产品推荐

