Normy 数据缓存自动更新的边界:为什么顶层数组仍需手动更新,以及 2 种实用应对策略
2026/8/22 14:53:41 网站建设 项目流程

Normy 数据缓存自动更新的边界:为什么顶层数组仍需手动更新,以及 2 种实用应对策略

【免费下载链接】normyAutomatic normalization and data updates for data fetching libraries (react-query, vue-query, trpc, swr, rtk-query and more)项目地址: https://gitcode.com/gh_mirrors/no/normy

如果你正在使用 react-query、SWR 或 RTK Query,那么 Normy 这个开源库值得了解:它负责对接口数据进行自动数据归一化,并在 mutation 成功后自动更新所有相关缓存,让你告别手动维护setQueryData的繁琐代码。但 Normy 的自动化有一个明确的边界——顶层数组的增删排序仍需手动处理。本文帮你看懂这个边界产生的原因,并给出 2 种简单实用的应对策略。

一、Normy 能自动做什么?先划清能力范围

Normy 的核心逻辑在 packages/normy/src/normalize.ts:所有带id的对象(无论嵌套多深)都会被按 id 存入一个共享的归一化存储,而各个查询的数据只保留对这些对象的"引用"。

于是当 mutation 返回{ id: '1', name: 'Name 1 Updated' }时,create-normalizer.ts 中的getQueriesToUpdate会:

  1. 找到所有"依赖"了id: 1这个对象的查询;
  2. 把归一化存储中的对象深合并(deep merge)为新值;
  3. 自动把新数据写回每一个相关查询的缓存。

一张表看懂 Normy 自动更新的边界

场景是否自动原因
修改对象的字段(如书名、作者名)✅ 自动按 id 匹配并深合并,所有依赖查询同步更新
更新嵌套对象列表(如书籍的likedByUsers✅ 自动列表属于对象内部属性,整体合并
顶层数组追加新对象❌ 手动新对象尚不在任何查询中,不存在依赖关系
顶层数组删除对象❌ 手动同一对象可能出现在多个查询,库无法猜你的意图
顶层数组重新排序❌ 手动顺序只存在于各查询自身,不在共享存储中

二、为什么顶层数组是个"例外"?

原因并不复杂,官方 README 在 Normalization of arrays 一节 里解释得很清楚:

  • ADD_BOOK:Normy 不知道新对象应该被加进哪一个查询的数组,也不知道加在第几个位置
  • REMOVE_BOOK:一本书可能同时出现在"全部书籍列表"和"我的收藏"等多个查询里,Normy 不知道你想从哪些列表里移除它;
  • SORT_BOOKS:数组的顺序信息只存在于各个查询自己的缓存中,共享的归一化存储里只有"对象内容",没有"数组成员关系"。

换个角度理解:Normy 的共享存储是一张"对象 id → 对象内容"的表,而"哪些对象出现在哪个列表、按什么顺序"是每个查询自己的事。所以**"内容"交给 Normy 自动同步,"成员关系"留给你手动决定**——这正是它的设计边界。

💡 注意:这个限制只针对顶层数组。只要对象列表是某个带 id 对象的属性(例如book.likedByUsers),mutation 返回更新后的完整对象即可自动生效。

三、应对策略 1:手动补"数组",自动补"字段"

最简单直接的做法:顶层数组用数据获取库自带的 API 手动更新,对象内容交给 Normy 自动同步,两者分工协作。

以 react-query 为例,添加新书时只需在 mutation 的onSuccess里手动把新对象拼进['books']这个查询的缓存(示例见 examples/react-query/src/components/app.jsx):

onSuccess: mutationData => { queryClient.setQueryData(['books'], data => data.concat(mutationData)); }

这之后,如果该书的作者信息又发生了变化,Normy 会自动把更新同步到书籍列表、书籍详情等所有相关查询——你完全不用手写map+ 判断 id 的代码。

🎯 适用场景:新增、替换单条数据等目标查询明确的操作,代码量最少。

四、应对策略 2:先用 getDependentQueries 定位,再批量更新

当一次操作影响多个查询的顶层数组时(比如从"全部列表""收藏""最近浏览"同时移除一本书),手动猜查询 key 很容易漏。这时可以利用 Normy 内置的依赖查询能力:

  • getDependentQueries:传入一段包含对象的数据(如{ id: '1' }),返回所有持有该对象的查询 key 列表;
  • getDependentQueriesByIds:直接传 id 数组,效果相同。

拿到 key 列表后,你可以用 react-query 的invalidateQueries让相关查询重新拉取,或逐个setQueryData从数组中过滤掉目标对象。

🛠 同样的能力在 SWR 和 RTK Query 集成中也有对应实现,可参考 packages/normy-swr/src/SWRNormalizerProvider.tsx 与 packages/normy-rtk-query/src/index.ts。

🎯 适用场景:删除、排序等跨多个查询的操作,避免"漏更新某个列表"的 bug。

五、小结:把 Normy 用在刀刃上

  • 对象字段变更→ 完全交给 Normy 自动更新,这是它最大的价值;
  • 顶层数组增删排序→ 手动处理,查询明确就用策略 1,跨多查询就用策略 2 先定位再批量更新;
  • 项目作者也表示,未来版本计划通过额外的"指针"机制把这些更新也自动化,值得持续关注 CHANGELOG。

理解这个边界后你会发现,Normy 的自动化覆盖了你日常 80% 以上的缓存同步场景,剩下的 20% 也有清晰的应对方案——这正是它"简单又实用"的地方。

【免费下载链接】normyAutomatic normalization and data updates for data fetching libraries (react-query, vue-query, trpc, swr, rtk-query and more)项目地址: https://gitcode.com/gh_mirrors/no/normy

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询