服务端状态与数据获取库技术选型对比:React Query vs SWR vs Apollo Client vs RTK Query vs React Router

服务端状态与数据获取库技术选型对比:React Query vs SWR vs Apollo Client vs RTK Query vs React Router 服务端状态与数据获取库技术选型对比React Query vs SWR vs Apollo Client vs RTK Query vs React Router【免费下载链接】query Powerful asynchronous state management, server-state utilities and data fetching for the web. TS/JS, React Query, Solid Query, Svelte Query and Vue Query.项目地址: https://gitcode.com/GitHub_Trending/qu/query本文以本仓库TanStack Query 一体化仓库内 docs/framework/react/comparison.md 中的官方对比文档为核心骨架对 React 生态中五个主流服务端状态方案进行逐维度横评并深入 React Query 在本仓库中的源码实现query-core 与 react-query 适配层解释表格中的每一项 ✅ 究竟靠什么代码实现。读完本文你将能读懂一张全量能力对比表背后的架构差异理解 React Query 的确定性缓存序列化、结构共享、渲染追踪等设计如何落地从而在真实项目中做出有依据的技术选型。对比的定位与符号约定该对比文档的初衷是尽可能准确、尽可能无偏表格结论面向所有被比较的库保持同一套标准社区贡献者可通过页面底部的编辑入口在附上证据后修正信息。比较的五个对象分别是库定位项目语境React QueryTanStack Query 的 React 实现本文所在仓库 packages/react-querySWRVercel 出品的轻量数据请求库外部独立项目不在本仓库内Apollo ClientGraphQL 官方阵营客户端外部独立项目不在本仓库内RTK QueryRedux Toolkit 内置数据获取方案外部独立项目不在本仓库内React Router路由 数据加载loader外部独立项目不在本仓库内对比表使用统一的 Feature/Capability Key 图例✅ 一等公民、内置、开箱即用无需额外配置或代码 支持但需借助非官方的第三方或社区库/贡献实现 官方支持且有文档但需要用户额外编写实现代码 未官方支持或未文档化。定位、数据源与缓存架构横评下表对比五者在平台绑定、数据形态、缓存模型上的底层差异。缓存架构的分歧是本次对比中最值得先读的部分因为它决定了上层几乎所有能力的实现成本。维度React QuerySWRApollo ClientRTK QueryReact Router开源仓库Star 徽章省略TanStack/queryvercel/swrapollographql/apollo-clientreduxjs/redux-toolkitremix-run/react-router平台依赖ReactReactReact、GraphQLReduxReact官方横向比较页无无无官方提供无支持的查询数据源Promise、REST、GraphQLPromise、REST、GraphQLGraphQL、AnyReactive VariablesPromise、REST、GraphQLPromise、REST、GraphQL支持框架ReactReactReact 及其他任意React缓存策略分层键 → 值Hierarchical Key→Value唯一键 → 值规范化 SchemaNormalized唯一键 → 值嵌套路由 → 值缓存键策略JSONJSONGraphQL QueryJSON路由路径缓存变更检测深比较键稳定序列化深比较键稳定序列化深比较键不稳定序列化键引用相等路由变更数据变更检测深比较 结构共享深比较经由 stable-hash深比较不稳定序列化键引用相等Loader 运行数据记忆化完整结构共享身份规范化身份身份身份包体积mingzip 实时徽章原表引用 Bundlephobia本文不复现外链同左同左同左react-router-dom history 两包合计API 定义位置组件内、外部配置均可组件内GraphQL Schema外部配置路由树配置缓存架构差异解读Hierarchical Key → ValueReact Query缓存以查询键 查询为单位组织查询键支持任意层级嵌套底层 QueryCache 把每个序列化后的键映射到一条查询状态。Unique Key → ValueSWR / RTK Query同样是键值缓存但 React Query 的键结构更强调前缀式的层级可匹配性详见下文 Partial Query Matching。Normalized SchemaApollo Client数据按实体扁平化存放entity → record以此规避高层数据重复这也是其自动变更新后重取能力的基础。Nested Route → ValueReact Router缓存生命周期与路由匹配绑定离开路由即释放见注解 8。值得注意的是Cache Change Detection列的差异React Query 与 SWR 对键本身做稳定序列化 深比较因此相同语义的键不会被重复缓存Apollo 的序列化相对不稳定RTK Query 使用键的**引用相等**判断缓存命中语义因此不同。查询编排与常规数据能力横评下表覆盖日常开发最常接触的查询编排能力。可以清晰看到 React Query 在此区间几乎全量 ✅而 React Router 因不缓存活动路由之外的数据在持续性数据能力上大量为 。维度React QuerySWRApollo ClientRTK QueryReact RouterQueries基础查询✅✅✅✅✅Cache Persistence缓存持久化✅✅✅✅8Devtools✅✅✅✅Polling/Intervals轮询/定时刷新✅✅✅✅Parallel Queries并行查询✅✅✅✅✅Dependent Queries依赖查询✅✅✅✅✅Paginated Queries分页查询✅✅✅✅✅Infinite Queries无限滚动查询✅✅✅✅Bi-directional Infinite Queries双向无限查询✅✅Infinite Query Refetching无限查询刷新✅✅✅Lagged Query Data新旧数据平滑过渡1✅✅✅✅✅Selectors选择器✅✅✅N/AInitial Data初始数据✅✅✅✅✅Scroll Recovery滚动位置恢复✅✅✅✅✅Cache Manipulation直接操作缓存✅✅✅✅Outdated Query Dismissal丢弃过期结果✅✅✅✅✅Stale While Revalidate✅✅✅✅Stale Time ConfigurationstaleTime 可配置✅7✅Window Focus Refetching窗口聚焦重取✅✅✅Network Status Refetching网络恢复重取✅✅✅✅缓存控制与渲染性能横评下表聚焦手动控制、渲染优化、离线与脱水平台能力这些维度最能拉开各库架构差距。维度React QuerySWRApollo ClientRTK QueryReact RouterRender Batching Optimization渲染批处理与优化2✅✅✅✅Auto Garbage Collection自动垃圾回收✅✅N/AMutation Hooks✅✅✅✅✅Offline Mutation Support离线变更✅Prefetching APIs预取 API✅✅✅✅✅Query Cancellation查询取消✅✅Partial Query Matching部分查询匹配3✅✅✅N/APre-usage Query/Mutation Configuration用前预配置4✅✅✅✅General Cache Dehydration/Rehydration缓存脱水/注水✅✅✅✅Offline Caching离线缓存✅✅React Suspense✅✅✅✅Abstracted/Agnostic Core框架无关核心✅✅✅Automatic Refetch after Mutation变更后自动重取5✅✅✅Normalized Caching规范化缓存6✅八条关键差异注解从符号到源码表格之外对比文档用 8 条注解释放了结论的前提与细节。下面逐条展开并对 React Query 侧给出本仓库源码级佐证。注解 1Lagged Query Data —— 分页 UI 不闪硬加载态的关键React Query 允许在新查询加载期间继续展示上一条查询的已有数据避免分页/无限加载场景中出现硬 Loading 态这也是 Suspense 未来要原生提供的体验。其他库除非已预取否则新查询期间会渲染硬加载态。该能力对分页与 Infinite UI 极其重要源码中由数据保留策略 状态派生共同支撑可关注 query-core 中 observer 对data/isPlaceholderData的状态组织。注解 2Render Optimization —— 精确到属性的订阅式渲染React Query 默认自动追踪组件实际访问了结果对象上的哪些字段仅在这些字段变化时才触发重渲染。实现上QueryObserver维护了一个属性追踪集合定义#trackedProps new Setkeyof QueryObserverResult()见 packages/query-core/src/queryObserver.ts#L65通过trackResult返回一个Proxy包装的结果对象get钩子里调用trackProp(key)把每次属性访问记入集合见 packages/query-core/src/queryObserver.ts#L258-L273。若想关闭该优化将notifyOnChangeProps设为all任何查询更新新数据、fetching 状态变化等都会重渲染组件。若只关心data或error可进一步设成[data, error]减少渲染次数。值得补充的是该配置还支持函数形式源码在通知分支中通过typeof notifyOnChangeProps function ? notifyOnChangeProps() : notifyOnChangeProps求值见 packages/query-core/src/queryObserver.ts#L648-L662可用于在渲染期间动态决定通知范围。此外 React Query 会批量合并更新当多个组件订阅同一条查询时一次状态变更只触发一次应用层渲染。这意味着数据访问越克制、组件重渲染越少。注解 3Partial Query Matching —— 前缀语义 过滤函数任意操作查询组由于 React Query 使用确定性查询键序列化你可以在不逐一罗列具体键的情况下批量操作一组查询。例如刷新所有以todos为键前缀的查询无论携带何种变量精确指定带/不带变量、或含嵌套属性的查询甚至传入过滤函数只匹配满足自定义条件的查询。支撑它的底层函数是 packages/query-core/src/utils.ts#L236-L269 的partialMatchKey它递归地只校验被匹配方b中存在哪些键、a 对应位置是否与之相等因此{ todos: [...] }这一前缀形态可以命中任意更深层的变体键。这是 SWR 仅能以 需自行实现支持、而 React Query 能以一等能力提供的重要原因。注解 4Pre-usage Query/Mutation Configuration —— 把配置沉淀到用之前这是使用前即可配置好查询/变更行为的能力别名。例如一条查询可以预先配置好默认值fetcher、staleTime、重试策略等真正使用处只写useQuery({ queryKey })无需每次重复传入 fetcher 或选项。SWR 只有全局默认 fetcher 的部分形态既不能按查询粒度配置也谈不上为 mutation 配置。在本仓库中这一能力以多重形态落地queryOptions()/mutationOptions()工厂见 packages/react-query/src/queryOptions.ts、packages/react-query/src/mutationOptions.ts把查询定义与调用点解耦、同时获得完整类型推断也可通过 QueryClient 的defaultOptions做全局/键粒度默认值见 packages/query-core/src/queryClient.tsAPI 定义位置表格第 11 行中 React Query 标为 Component, External Config即组件内联定义与外部集中定义都受支持正源于此。注解 5Automatic Refetch after Mutation —— 真正自动依赖 Schema要实现真正意义上的变更后自动重取库需要依赖Schema例如 GraphQL 提供的 schema及识别实体的启发式规则才能知道一次变更影响了哪些实体、进而重取相关查询。因此 Apollo基于 GraphQL Schema 规范化缓存能标 ✅而 React Query、SWR、RTK Query 均不提供这种魔法式的全自动刷新React Query 给出的路径是invalidateQueries等显式缓存失效 API属于半自动、可控性优先的设计哲学标 。注解 6Normalized Caching —— 刻意不做的规范化React Query、SWR、RTK Query目前都不支持自动规范化缓存即以扁平实体存储避免高层数据重复。React Query 的定位是把数据获取 缓存生命周期做好实体关系建模仍交给开发者如用 selector 做 denormalize。这也解释了表格中 React Query 的 Cache Persistence、Dehydration/Rehydration、离线能力为何全部 ✅ —— 因为它缓存的是完整查询响应而非依赖 Schema 的碎片实体天然易于整体持久化。注解 7SWR 的 Immutable Mode —— 不替代 staleTimeSWR 自带 immutable 模式可让查询在缓存生命周期内只 fetch 一次但它没有 stale-time 概念也没有条件式自动重新验证。也就是说SWR 无法表达数据在 X 毫秒内视为新鲜、过期后按需重取这类时间窗口语义React Query 的staleTime与 RRstale-while-revalidate模型是独立的、按查询可配置的。注解 8React Router 的缓存持久化边界React Router不会缓存超出当前匹配路由之外的数据一旦离开某条路由其 loader 数据即被丢弃Nested Route → value 模型决定因此没有跨页面缓存Cache Persistence、Devtools、轮询、Infinite、Cache Manipulation 等持续型能力自然为 。React Query 优势特性在源码中的落点对选型者而言看懂React Query 为什么能在多数行拿 ✅比背表格更有价值。以下能力在本仓库均可直接定位到实现文件。确定性缓存键稳定序列化的hashKeyReact Query 的缓存键变更检测依赖键本身被稳定序列化。默认哈希函数 packages/query-core/src/utils.ts#L219-L234 的hashKey实现方式是对值做JSON.stringify且在 stringify 的 replacer 中对每个普通对象按键名排序后再输出。因此{ b: 1, a: 2 }与{ a: 2, b: 1 }会生成相同哈希——相同语义的查询键被当作同一缓存条目开发者还可通过queryKeyHashFn注入自定义哈希。这正是表格 Cache Change Detection: Deep Compare Keys (Stable Serialization) 的源码依据。数据记忆化replaceEqualDeep的结构共享React Query 的 Data Memoization 为 Full Structural Sharing新数据与旧数据深比较后能复用的子树直接复用旧引用避免无意义重渲染。核心函数是 packages/query-core/src/utils.ts#L273 起的replaceEqualDeep若a b引用相等或原始值相等直接返回a深比较失败时仅替换 b 中与 a 不等的子节点其余子节点保持 a 的引用。该函数在 utils.test.tsx 中有成组的单元测试覆盖含原始值、Date、数组、对象、undefined 等各种边界并可通过查询选项structuralSharing: false关闭或传入自定义函数见 queryClient.test.tsx 对structuralSharing自定义的验证。渲染追踪与批量更新Proxy notifyOnChangeProps如注解 2 所述trackResult以 Proxy 记录属性访问见 queryObserver.ts#L258-L273QueryObserver在通知时比对本次实际变化字段与组件访问过的字段集合。相关状态分支集中在 queryObserver.ts#L648-L662支持all、字段数组、函数三种形态。React 适配层见 packages/react-query/src/useBaseQuery.ts负责在渲染期间把追踪到的属性传给 observer形成端到端的按需渲染闭环。自动垃圾回收gcTime驱动的 Query 生命周期React Query 的 Auto Garbage Collection 由 Query 自身的生命周期管理实现查询在失去所有订阅者后进入倒计时gcTime原cacheTime到期即被清理并释放订阅。源码在 packages/query-core/src/query.ts#L211 通过updateGcTime(this.options.gcTime)将选项写入 query 实例QueryCache 据此调度回收。这解释了为何 React Query 既能用后即焚控制内存又能配合持久化中间件做离线长期留存。双向无限查询getNextPageParam/getPreviousPageParamInfinite Queries 及双向能力由 packages/query-core/src/infiniteQueryBehavior.ts 支撑前向翻页读取getNextPageParam的返回并推进pageParam后向翻页则基于getPreviousPageParam与initialPageParam从已有首页向前补页见该文件 L132-L152 的取参逻辑并对外暴露hasNextPage/hasPreviousPage判断L164-L175。React 层入口为 packages/react-query/src/useInfiniteQuery.ts。SWR/Apollo 需要自行组合实现的方向性逻辑在这里是内置语义。取消、预取、缓存操作QueryClient的统一 API表格中 Query Cancellation、Prefetching、Cache Manipulation、Dehydration/Rehydration 等 ✅ 项都收敛在 packages/query-core/src/queryClient.ts 暴露的prefetchQuery、fetchQuery、cancelQueries、removeQueries、invalidateQueries、setQueryData、getQueryCache().find/findAll等 API 上。其行为在 packages/query-core/src/tests/queryClient.test.tsx 中由大量测试锁定例如prefetchQuery后的缓存命中L1851 附近、removeQueries精确/前缀删除L1941 附近、cancelQueries配合revert选项L1986、L2003 附近。React 侧另有独立于组件的usePrefetchQuery/usePrefetchInfiniteQuery见 packages/react-query/src/usePrefetchQuery.tsx。持久化、Devtools 与框架无关核心持久化 / 离线persistQueryClient系列位于 packages/react-query-persist-client/src底层契约定义在 packages/query-persist-client-core/src配合 packages/query-sync-storage-persister 等存储适配器即可实现 Cache Persistence、Dehydration/Rehydration 与 Offline Caching。Devtools独立包 packages/react-query-devtools/src另有 packages/query-devtools 承载核心面板Devtools 一行 ✅。Abstracted/Agnostic Core这是 React Query 架构上区别于 SWR/React Router 的关键点。本仓库根下 packages/query-core 是纯框架无关核心而 packages/react-query、packages/solid-query、packages/svelte-query、packages/vue-query、packages/preact-query、packages/angular-query-experimental 只是薄薄的框架适配层。查询键、缓存、垃圾回收、重试、轮询等复杂逻辑只写一遍各框架共享同一套测试与语义——这正是对比文档在同仓库语境下称 React Query 具备 Abstracted/Agnostic Core 的直接依据。如何利用这份对比做决策把表格与注解还原为决策建议时请基于自身约束而非谁的行最多需要类 Schema 规范化缓存 GraphQL 深度集成的项目→ Apollo Client 的 Normalized Caching 与变更后自动重取是结构性优势已经全面使用 Redux、希望状态与数据获取同源管理→ RTK Query 与 Redux 生态集成最顺滑且同样是外部配置式 API团队只需一个极轻量的 fetch-on-render 层→ SWR 体积与心智负担更小但要接受没有 staleTime、GC、Query Cancellation 等进阶能力数据生命周期应当跟随路由、以 loader 驱动页面数据→ React Router 的嵌套路由数据模型最匹配但要清楚离开路由即失缓存的边界注解 8需要 Server-State 完整生命周期缓存、失效、后台刷新、GC、持久化、并发/依赖/无限查询、Devtools且希望知识沉淀为框架无关资产→ React Query 的能力矩阵与架构QueryClient API、queryOptions 预配置、partial key 失效、structural sharing、Suspense 支持等提供了当前对比中最完整且最可组合的集合。需要特别强调的是任何第三方能力声明例如 SWR 内部基于stable-hash的深比较均以各库官方文档与源码为准本文中的 React Query 侧结论均有上文标注的本仓库文件可作为一手证据。同时无偏也意味着承认其边界——如注解 6 所述React Query刻意不提供自动规范化缓存这与 Apollo 是取舍而非高下。结语一份横向对比表的真正价值不在于谁赢了几行 ✅而在于它逼你直面五个关键架构分叉缓存是键值、层级还是规范化数据生命周期绑定查询还是路由序列化是否稳定、变更检测是深比较还是引用相等更新是否批量、渲染是否可按属性订阅核心是否框架无关、能力是否可跨框架复用以本仓库 docs/framework/react/comparison.md 为基础结合 query-core 的源码实现你可以把表中的每一个符号都还原成一段可以阅读、可以测试、可以评估的真实设计从而为自己的项目做出有依据的工程决策。【免费下载链接】query Powerful asynchronous state management, server-state utilities and data fetching for the web. TS/JS, React Query, Solid Query, Svelte Query and Vue Query.项目地址: https://gitcode.com/GitHub_Trending/qu/query创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考