执行Mutation后如何在不使用refetchQueries的情况下更新本地ObservableQuery状态?(基于Svelte-Apollo)
在Svelte-Apollo中执行Mutation后更新本地查询缓存的最优方案
我之前也遇到过完全一样的问题——用watchQuery拿到列表数据,执行新增Mutation后缓存里有数据,但视图就是不更新,不想用refetchQueries因为太浪费请求。下面分享两个经过验证的可行方案,核心思路都是手动告诉Apollo缓存要把新条目合并到指定的查询结果中:
第一步:先修正你的Mutation(关键前提)
你的addItem Mutation返回的字段太少了,缺少列表渲染必须的id(你在each里用item.id作为key),还有created_at这类列表用到的字段。首先要修改Mutation,确保返回的新条目结构和查询结果一致:
mutation addItem($name: String!, $description: String) { createItem( input: { data: { name: $name, description: $description } } ) { item { id # 必须添加,缓存需要唯一标识 name description created_at # 和查询结果保持结构一致 } } }
方案一:用Mutation的update函数手动更新查询缓存
这是最直接的方式,在定义Mutation时传入update函数,它会在Mutation成功后拿到缓存实例和返回数据,你可以手动把新条目写入到GetItems查询的缓存中:
<script> import { mutation, query } from 'svelte-apollo'; import GetItems from './queries/GetItems.gql'; import AddItem from './queries/AddItem.gql'; let search = ''; // 响应式的查询变量 $: queryVars = { sort: 'created_at:DESC', search }; $: itemsQuery = query(GetItems, { variables: queryVars }); // 定义带update逻辑的Mutation const addItem = mutation(AddItem, { update: (cache, { data: { createItem } }) => { const newItem = createItem.item; // 读取当前查询的缓存数据 const existingData = cache.readQuery({ query: GetItems, variables: queryVars // 必须和watchQuery的变量完全一致! }); if (existingData) { // 只把符合当前搜索条件的新条目加到列表前面(保持排序) const matchesSearch = newItem.name.includes(search); if (matchesSearch) { cache.writeQuery({ query: GetItems, variables: queryVars, data: { items: [newItem, ...existingData.items] } }); } } } }); async function handleAdd(name, desc) { await addItem({ variables: { name, description: desc } }); // 不需要额外操作,update函数已经处理了缓存更新 } </script>
方案二:用cache.modify灵活修改缓存字段
如果你不想重新写入整个查询结果,可以用cache.modify直接修改缓存中的列表字段,这种方式更适合复杂场景(比如分页、动态过滤):
<script> // ... 其他代码和上面一致 const addItem = mutation(AddItem, { update: (cache, { data: { createItem } }) => { const newItem = createItem.item; cache.modify({ // 指定要修改的查询和变量,定位到对应的缓存条目 query: GetItems, variables: queryVars, fields: { // 这里的items对应查询中的字段名 items(existingItems = [], { readField }) { // 先检查新条目是否已经存在(避免重复) const isDuplicate = existingItems.some(item => readField('id', item) === newItem.id); if (isDuplicate) return existingItems; // 只添加符合当前搜索条件的条目 if (newItem.name.includes(search)) { return [newItem, ...existingItems]; } return existingItems; } }, // 确保通知所有订阅该缓存的watchQuery更新 broadcast: true }); } }); </script>
为什么之前缓存有数据但视图不更新?
Apollo缓存是归一化的——Mutation成功后只会自动更新新条目本身的缓存,但不会自动把它加到你的GetItems查询结果列表里。watchQuery订阅的是特定变量下的查询结果缓存,所以必须手动告诉Apollo:把这个新条目合并到该查询的列表中,这样$itemsQuery.data才会更新,Svelte的响应式视图也会随之刷新。
内容的提问来源于stack exchange,提问作者Leoneed
相关产品推荐
相关产品推荐

