Apollo Client中如何让GraphQL查询复用已缓存的菜单数据?
问题背景
React应用使用Apollo Client处理GraphQL查询,初始化时通过GetMenu查询获取完整菜单并缓存,但后续调用GetMenuSections查询获取顶级菜单板块时,无法从缓存读取数据,设置fetchPolicy: 'cache-only'也失败,必须走网络请求。
两个查询代码如下:
GetMenu 查询(初始化用)
query GetMenu( $menuId: ID $restaurantId: ID! $filter: MenuSectionFilterInput ) { getMenu(menuId: $menuId, restaurantId: $restaurantId) { id sections(filter: $filter) { ...Section sections { ...Section } } } } fragment Section on MenuSection { id items(restaurantId: $restaurantId) { ...Item } }
GetMenuSections 查询(后续用)
query GetMenuSections($restaurantId: ID!, $getMenuFilter: MenuFilterInput) { getMenu(restaurantId: $restaurantId, filter: $getMenuFilter) { id sections { name id } } }
核心问题解析
查询参数直接影响缓存匹配:Apollo Client默认将「查询名称 + 序列化后的完整参数」作为查询结果的缓存键。两个查询的参数结构、名称都不匹配:
GetMenu给getMenu传了menuId,没传filter;GetMenuSections给getMenu传了filter,没传menuId- 参数名称也存在差异(如
$filtervs$getMenuFilter)
这导致Apollo将两个查询视为完全独立的请求,不会复用缓存。
字段请求差异不阻碍缓存读取:两个查询请求的
sections字段内容不同(一个包含嵌套sections和items,一个只包含name和id),这本身不影响缓存读取——Apollo会自动从缓存的对象中提取所需字段,核心问题还是参数不匹配。
解决方案
方案1:调整查询参数,对齐缓存键
如果初始化调用GetMenu时menuId传的是null或固定值,修改GetMenuSections查询,给getMenu加上相同的menuId参数,对齐参数结构:
query GetMenuSections($restaurantId: ID!, $menuId: ID = null) { getMenu(restaurantId: $restaurantId, menuId: $menuId) { id sections { name id } } }
调用该查询时,传入和初始化时一致的menuId(比如null),Apollo就能匹配到之前的缓存键,直接从缓存读取数据。
方案2:配置极简Type Policies(推荐)
给Query类型的getMenu字段设置缓存键规则,只以restaurantId作为匹配依据,忽略其他参数:
const client = new ApolloClient({ // 你的其他配置(uri、cache等) cache: new InMemoryCache({ typePolicies: { Query: { fields: { getMenu: { // 指定仅用restaurantId作为缓存键 keyArgs: ['restaurantId'], }, }, }, }, }), });
这样只要两个查询的restaurantId相同,Apollo就会复用缓存的getMenu数据,无需调整查询参数。
方案3:手动指定缓存读取(@client指令)
在GetMenuSections查询中用@client指令强制从缓存读取,前提是缓存中已存在对应参数的getMenu对象:
query GetMenuSections($restaurantId: ID!, $menuId: ID = null) { getMenu(restaurantId: $restaurantId, menuId: $menuId) @client { id sections { name id } } }
内容的提问来源于stack exchange,提问作者Stretch0

