React Query与SWR尚能饭否?React Query还真香!
Author:zhoulujun Date:
为什么选React Query ?
React Query 是什么?React Query 是由@TannerLinsley 创建的 npm 库。
https://cloud.tencent.com/developer/article/2171475
「Tanner Linsley」主要作品是React Table与React Query,从一个开源项目到庞大的开源矩阵,他是怎么做到的?
TanStack矩阵中的TanStack Query(即React Query)的官方课程[3]已经售出8w份了,按当前的折扣价156刀算,这部分收入有税前1200w刀了。
虽然实际收入肯定达不到这个数,但数百万刀的收益还是有的。
它是一个针对 React 应用的状态管理器,可以简化许多任务,例如处理 HTTP 请求状态、在客户端保存数据以防止多次请求、使用 hooks 共享数据等等。从这个角度来讲、来理解这个库,可能会认为它是axios加强版。但是,也不止于此!
提供了服务端状态的:拉取、同步、更新操作的 Hook 化封装:
Query 缓存(前端提升性能最重要的策略),无需手动处理数据请求和缓存管理逻辑,而是将这些复杂的过程交给工具来处理。
请求依赖(并行、串行依赖)解耦,可以将多个请求依赖,转化为一个请求依赖
增加了请求是否「过期」的判定,可以增加不同机制的过期判定
直接关联 ReactComponent 状态,立刻生效而无需额外一行代码
内置支持分页、懒加载、长轮询、无限滚动等场景
管理内存和垃圾数据集的回首操作
支持按照 query 格式存储结构化的信息
…
处理 ServerSideState 在客户端的管理,能够实现 ServerSate 和 ClientState 解耦。
ReactQuery 和 Redux 之类的状态库的关系比较微妙,通过剥离「SeverState」,发现 ClientState 比较少的话,其实 Redux 等状态管理库使用意义也就不大了。可以通过 ReactQuery 来将这些 ServerSide 轻松用少量的代码就能够管理到极致。
按照来源,前端有两类「状态」需要管理:
用户交互的中间状态,比如组件的isLoading、isOpen,这类「状态」的特点是:
以「同步」的形式更新
「状态」完全由前端控制
「状态」比较独立(不同的组件拥有各自的isLoading)
服务端状态
通常以「异步」的形式请求、更新
「状态」由请求的数据源控制,不由前端控制
「状态」可以由不同组件共享,作为可以由不同组件共享的「缓存」,还需要考虑更多问题,比如:
缓存失效
缓存更新
在陈年的老项目中,通常用Redux、Mobx这样的「全局状态管理方案」无差别对待他们。
在后端看来,后端负责提供数据,前端负责展示数据,那么:
数据更新后,前端应该如何渲染?
数据失效后,前端应该如何渲染?
本质来说,这是个「数据/缓存同步」的问题,只不过在SPA时代,这个问题刚好交给前端解决而已。
但是,后端天生离数据更近,解决这个问题更有优势。所以当渲染任务逐渐移向后端,React-Query(或类似的库)便逐渐失去市场。
总结来说:取代React-Query的,并不是更先进的竞品,而是他存在的土壤正在逐渐消失。
传统的SSR主要应用在数据的首屏渲染。当首屏渲染完成,数据的后续同步操作还是发生在前端。
所以,React-Query还是有用武之地。
类似的,在全栈框架Next.js中,也推荐在CSR(客户端渲染)时使用同团队开发的缓存库SWR用于数据的同步操作。
SWR VS TanStack/Query
React SWR于2019年发布,专注于为React开发者提供一个最小的、开发者友好的API,来调整缓存系统和行为。
SWR (State While Re-validate,是HTTPRFC 5861中的一个通用缓存原则)短小精悍,仅有247kb。
但TanStack/Query 更受欢迎(发布于React推出后的一年,2014),功能齐全,并且社区营销做的好,有完善的配套课程。而且开发团队计划为其他前端库提供官方适配器包,如Vue Query、Svelte Query等。
SWR是一个轻量级的React Hook库,它提供了一个名为useSWR的Hook,通过它可以方便地使用缓存、重试和错误处理等功能。它使用了Stale-While-Revalidate(过期时间内的缓存,同时后台请求新数据),以及Cache Invalidation(定期更新数据)这些概念,它的工作原理是在请求数据时,先返回本地缓存的数据,同时发起异步请求来获取最新的数据。当新数据到达时,SWR会使用React的useState和useEffect Hooks自动更新组件状态。
TanStack/Query则是一个完整的状态管理解决方案,它是一个基于GraphQL的全局状态管理器,它提供了一种声明式的方式来定义和管理数据状态。它使用了一个名为Query Client的组件,可以在应用程序中全局共享。它的工作原理是使用GraphQL风格的查询语言来定义数据查询,Query Client会根据查询中指定的参数自动发起请求。它还提供了缓存和标准化响应数据的功能,这使得多个组件可以共享相同的数据。
他们主要区别还是在于缓存机制
缓存机制
SWR使用了Stale-While-Revalidate(SWR)缓存策略来实现数据缓存和刷新。它会缓存请求的数据,并在过期之前返回缓存的数据。同时,在后台异步请求数据,以确保数据的最新性。
TanStack/Query使用了标准化的缓存机制,将响应数据规范化并存储在全局状态管理器中。这意味着在多个组件中共享相同的数据时,只需要发出一次请求,从而减少了网络请求和提高了性能。并在组件卸载时自动清除数据。
所以,SWR并不要求对后端 API 进行特殊的改造或支持。它可以与任何符合 RESTful API 设计原则的后端进行配合使用,通过发送 HTTP 请求来获取数据,并自动处理数据的缓存和更新。
但是TanStack/Query 则是一个针对数据查询和状态管理的完整解决方案,它基于 GraphQL 构建。GraphQL 是一种用于 API 查询和数据操纵的查询语言和运行时。在使用 TanStack/Query 时,后端需要提供一个符合 GraphQL 规范的服务,以便前端可以通过 GraphQL 查询语句来获取所需的数据。后端的 GraphQL 服务会解析和处理这些查询,并返回相应的数据。
useQuery
useQuery。通过它,你可以以一种非常简单的方式从源中检索数据并处理此请求的所有状态。
const fetchTodoList= async (): Promise<Todo[]> => { const response = await fetch('api/tasks'); if (!response.ok) throw new ResponseError('Failed to fetch todos', response); return await response.json(); }; function Todos() { const { isPending, isError, data, error } = useQuery({ queryKey: ['todos'],//查询关键字,能够存储结果并在应用程序的不同部分中使用它。 queryFn: fetchTodoList,//查询函数,从源(rest、GraphQL 等等)检索数据的方法。 {refetchOnWindowFocus: false,retry: 2}//配置项,重试次数、何时刷新数据、如何缓存数据等等 }) if (isPending) { return <span>Loading...</span> } if (isError) { return <span>Error: {error.message}</span> } // We can assume by this point that `isSuccess === true` return ( <ul> {data.map((todo) => ( <li key={todo.id}>{todo.title}</li> ))} </ul> ) }
useQuery 本身并不提供请求能力,而是依赖 Fetch API 或 axios 三方库提供请求能力,useQuery() 做的就是提供响应数据存储和请求状态的包装。
具体文档阅读:https://tanstack.com/query/latest/docs/framework/react/guides/queries
Query Keys
At its core, TanStack Query manages query caching for you based on query keys.
Query keys have to be an Array at the top level, and can be as simple as an Array with a single string, or as complex as an array of many strings and nested objects. As long as the query key is serializable, and unique to the query's data, you can use it!
React Query 的整体架构包含以下几个核心组件:
QueryCache:缓存 Query 的结果,提高查询的性能。
QueryKey:用于唯一标识一个 Query 的键。
QueryFunction:定义一个数据查询请求的函数,可以使用各种数据源,例如 REST API、GraphQL、WebSocket 等。
QueryResult:表示一个 Query 的结果,包括 loading、error、data 等状态。
QueryClient:管理 Query Cache 的实例,包括添加、移除、更新、取消、重试等操作。
QueryObserver:观察 Query 的状态变化,包括 loading、error、data 等状态。
QueryHook:使用 React Hooks 的方式定义一个 Query,包括 useQuery、useMutation、usePaginatedQuery 等。
useMutation
useQuery 还为我们做了如下工作:
多个组件请求同一个query时只发出一个请求
缓存数据失效/更新策略(判断缓存合适失效,失效后自动请求数据)
对失效数据垃圾清理
数据的CRUD由2个hook处理:
useQuery处理数据的查
useMutation处理数据的增/删/改
function App() { const {data, isLoading, isError} = useQuery('userData', () => axios.get('/api/user')); // 新增用户 const {mutate} = useMutation(userData => axios.post('/api/user', userData), { onSuccess: () => { queryCache.invalidateQueries('userData')//将userData对应的query缓存置为invalidate } }) return ( <ul> {data.map(user => <li key={user.id}>{user.name}</li>)} <button onClick={() => { mutate({name: 'kasong', age: 99})} }>创建用户</button> </ul> ) }
useQueries
并行请求及依赖请求,useQueries可以解决
const queryResults = useQueries({ queries: [ { queryKey: ["api", "queryOne"], queryFn: fetchData }, { queryKey: ["api", "queryTwo"], queryFn: fetchData } ] })
参考文章:
前端类库 - SWR 还是 @tanStack/query,哪个是更好的选择? https://zhuanlan.zhihu.com/p/633825403
React-Query:啥都没干,就被淘汰了? https://zhuanlan.zhihu.com/p/632335371
react-query手把手教程②-深入查询键及查询函数 https://segmentfault.com/a/1190000041951931
转载本站文章《React Query与SWR尚能饭否?React Query还真香!》,
请注明出处:https://www.zhoulujun.cn/html/webfront/ECMAScript/jsBase/2024_1028_9311.html