在Apollo Cache中,何时使用read/writeQuery与read/writeFragment?
Apollo Cache: readQuery/readFragment 与 writeQuery/writeFragment 的区别及适用场景
你观察到两者在单个实体读写时结果一致,但它们的核心差异在于访问缓存的方式,而非仅仅返回形式。以下是具体区别和适用场景:
一、Read 操作:readQuery vs readFragment
核心差异
readQuery:通过完整的GraphQL查询结构(从根字段开始)定位缓存数据,需要符合Schema定义的查询语法,支持变量传参。readFragment:直接通过缓存实体的组合ID(格式为__typename:id,比如Track:c_1)定位单个实体,跳过根字段的查询路径,直接读取实体数据。
适用场景
使用 readQuery 的情况:
- 复用现有查询:如果组件已通过
useQuery定义了某个查询,直接复用该查询读缓存,无需重新编写fragment// 假设组件中已定义该查询 const GET_TRACK = gql` query getTrack($trackId: ID!) { track(id: $trackId) { id title author { name } } } `; // 直接复用查询读缓存 const { track } = client.readQuery({ query: GET_TRACK, variables: { trackId: 'c_1' } }); - 查询根字段下的批量/关联数据:比如读取所有Track列表,或从根字段获取嵌套的复杂数据结构
const { tracks } = client.readQuery({ query: gql` query getAllTracks { tracks { id title modulesCount } } ` }); - 依赖动态变量定位数据:根据路由参数、用户输入等动态值查询对应的实体,通过
variables传递参数更灵活
使用 readFragment 的情况:
- 已知实体缓存ID:当你已持有实体的
__typename和id(比如之前查询返回的结果),直接通过组合ID读取更高效,无需构建完整查询// 假设已从其他查询拿到track实体的__typename和id const trackCacheId = `${track.__typename}:${track.id}`; const track = client.readFragment({ id: trackCacheId, fragment: gql`fragment TrackBasic on Track { id title }` }); - 仅需单个实体的部分字段:无需从根字段层层定位,直接针对目标实体定义需要的字段,代码更简洁
- 复用通用片段:将常用的实体字段抽成通用fragment,在多个组件/场景中复用,避免重复编写字段列表
二、Write 操作:writeQuery vs writeFragment
核心差异
writeQuery:通过完整的查询结构更新缓存,需要匹配查询的字段结构,会更新查询路径上的所有相关缓存节点writeFragment:直接通过实体组合ID更新单个实体的缓存字段,仅修改目标实体的指定字段,不影响其他缓存节点
适用场景
使用 writeQuery 的情况:
- 更新根字段关联的数据:比如更新整个Track列表,或根字段下的嵌套结构,确保缓存与查询结构一致
client.writeQuery({ query: gql`query getAllTracks { tracks { id title } }`, data: { tracks: [...existingTracks, newTrack] // 更新列表数据 } }); - 同步组件查询的缓存:如果组件使用某个查询渲染数据,用相同的查询结构更新缓存,可确保组件自动接收缓存更新并重新渲染,避免缓存不一致
- 批量更新关联实体:通过查询结构一次性更新多个关联实体(比如更新Track的同时,更新其Author的信息)
使用 writeFragment 的情况:
- 明确目标实体的缓存ID:直接更新单个实体的部分字段,代码更简洁,无需构建完整查询
client.writeFragment({ id: 'Track:c_1', fragment: gql`fragment TrackTitle on Track { title }`, data: { __typename: 'Track', id: 'c_1', title: 'Buy grapes 🍇' } }); - 批量更新同类型实体:循环处理多个同类型实体时,每个实体用对应的组合ID调用
writeFragment,逻辑更清晰updatedTracks.forEach(track => { client.writeFragment({ id: `${track.__typename}:${track.id}`, fragment: gql`fragment TrackBasic on Track { id title }`, data: track }); }); - 最小化缓存修改:仅更新实体的指定字段,避免影响其他无关的缓存节点,减少不必要的缓存更新触发
内容的提问来源于stack exchange,提问作者Velidan
相关产品推荐
相关产品推荐

