随着我们的项目越来越复杂,用户对速度和体验的要求也越来越高,现在单纯用axios或者fetch这些传统请求库来处理数据请求会产生很多额外的模板代码,已被诟病久矣,这个时候就需要使用请求增强库了。
可能有些同学会问了:“那什么是请求增强库呢?”,请求增强库一般是对传统请求库的上层封装,以hooks的形式深度绑定react、vue等UI框架,以及提供额外的性能增强特性例如响应缓存等,来达到减少模板代码、提高请求性能的目的。
而目前比较主流的请求增强库要数@tanstack/react-query、swr以及新秀alova了,它们都有不同的特点。在这里,我们来深度对比它们,你的项目更适合哪款增强工具,这样你下次遇到项目需求的时候,就能知道该如何应对了。
对比工具快速通道
咱们这就开工,从以下8个角度对比它们。
- 产品定位和特性
- hooks
- 框架支持
- 服务端支持
- 请求方式
- 缓存策略
- API自动化方案
- 性能表现
定位和特性的对比
首先是它们的定位不同。
@tanstack/react-query 主要是管理异步状态,它的强项在于能够帮你处理数据的获取、缓存和同步,你需要数据它帮你拿。数据过期了它帮你更新。
swr 侧重于异步状态管理,采用轻量级Hooks简化数据获取。
alova 提供完整的请求方案,主要帮助开发者提升API集成效率,它将开发者使用API从7个步骤降低为只需要1个步骤,帮助开发者减少大量API维护的时间。
不同的定位意味着它们在设计上会有不同的考虑,react-query和swr以提供的hooks以及缓存操作函数为主,alova从整体的请求方案入手优化API消费效率,因此它不仅提供了hooks,还提供了hooks中间件、全局的无感刷新拦截器等,都被称为请求策略,还有API自动生成和查找扩展插件,因此我觉得它在多个方面上解决了API的问题。
让我们来看一个对比的react示例。
react-query
import { useQuery } from '@tanstack/react-query';
const fetchUsers = async () => fetch('https://api.example.com/users')
.then(res => res.json());
function UserList() {
const { data: users, isLoading, error } = useQuery('users', fetchUsers);
if (isLoading) return <div>Loading...</div>;
if (error) return <div>An error occurred: {error.message}</div>;
return (
<ul>
{users.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
}
export default UserList;
swr
import useSWR from 'swr';
const fetcher = url => fetch(url)
.then(res => res.json());
function UserList() {
const { data: users, error } = useSWR('https://api.example.com/users', fetcher);
if (!users) return <div>Loading...</div>;
if (error) return <div>An error occurred: {error.message}</div>;
return (
<ul>
{users.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
}
export default UserList;
alova
import { createAlova, useRequest } from 'alova';
import adapterFetch from 'alova/fetch';
import ReactHook from 'alova/react';
const alovaInstance = createAlova({
baseURL: 'https://api.example.com',
statesHook: ReactHook,
requestAdapter: adapterFetch(),
});
function UserList() {
const methodInstance = alovaInstance.Get('/users');
const { loading, data: users, error } = useRequest(methodInstance);
if (loading) return <div>Loading...</div>;
if (error) return <div>An error occurred: {error.message}</div>;
return (
<ul>
{users.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
}
export default UserList;
可以看到,react-query、swr是直接以异步函数发送请求,alova需要提前创建一个请求实例,里面定义了在react下、使用fetch适配器来发送请求,此外还提供了xmlhttprequest、axios等适配器,这些都是因为它们的定位主导的设计理念,这些不同还会在后面的比较中体现。
hooks的比较
hooks数量
-
@tanstack/react-query 提供了
useQuery、useMutation和useInfiniteQuery等 hooks。 -
swr 主要提供
useSWR、useSWRInfinite等hooks。 -
alova 提供不同请求场景下共15+请求策略,包括
useRequest,useWatcher,usePagination,useFetcher,useForm等。
hooks操作函数
alova 的hooks的特点之一,在于它的场景化hooks还提供了丰富的操作函数,这些函数允许开发者更精细地控制数据操作,快速实现不同复杂请求的完整的功能,而这是 react-query 和 swr 所没有的,例如以下是分页请求的操作函数。
import { usePagination, useForm } from 'alova/client';
// 分页请求示例
const {
loading,
data,
page,
pageSize,
pageCount,
total,
insert,
remove,
replace,
refresh,
reload
} = usePagination(
(page, pageSize) => getUserList({ page, pageSize }),
{
initialPage: 1,
initialPageSize: 10,
}
);
// 使用操作函数
const handleAddUser = (newUser) => {
insert(newUser);
};
const handleRemoveUser = (userId) => {
remove(user => user.id === userId);
};
hooks中间件支持
alova 和swr的hooks支持中间件,允许在请求过程中进行更细粒度的自定义。但 react-query不支持。
swr
function myMiddleware (useSWRNext) {
return (key, fetcher, config) => {
// hook 运行之前...
// 处理下一个中间件,如果这是最后一个,则处理 `useSWR` hook。
const swr = useSWRNext(key, fetcher, config)
// hook 运行之后...
return swr
}
}
<SWRConfig value={{ use: [myMiddleware] }}>
// 或...
useSWR(key, fetcher, { use: [myMiddleware] })
alova
import { useRequest } from 'alova/client';
// 定义一个中间件
const loggingMiddleware = async (context, next) => {
console.log('Request started:', context.config);
const response = await next();
console.log('Request ended:', response);
};
// 在 hook 中使用中间件
const { loading, data } = useRequest(getUserList, {
middleware: loggingMiddleware
});
框架支持
客户端
- @tanstack/react-query 以独立包的形式分别支持了主流的UI框架,虽然它最初是为 React 设计的。
- react:
@tanstack/react-query - vue:
@tanstack/vue-query - svelte:
@tanstack/svelte-query - angular:
@tanstack/angular-query - solid:
@tanstack/solid-query
- react:
- swr 仅支持react。
- alova 以单包+UI框架适配器支持了
react/vue/svelte等框架,统一了各UI框架下的使用体验。
SSR
三者都支持主流SSR框架。
服务端支持
@tanstack/react-query 和 swr 主要提供客户端应用程序的hooks,虽然它们在服务端渲染(SSR)环境中可以使用,但它们不能直接在非 SSR 的服务端环境中使用,但它们并不提供在纯服务端环境(如 Express 或 Koa 路由处理程序)中使用的 API。
相比之下,alova 不仅完整支持了服务端程序,还为服务端环境提供了服务端请求策略,它们被称为server hooks,使得在服务端控制请求变得更加简单和灵活。
以下是一个带重试的验证码发送示例:
const { retry, sendCaptcha } = require('alova/server');
app.get('/api/captcha/send', async (req, res) => {
const email = req.params.email;
// 创建一个发送验证码的method实例
const captchaMethod = alovaInstance.Post('/api/captcha', {
email,
content: 'captcha content'
});
// 使用retry hook包装captchaMethod
const retringMethod = retry(captchaMethod, {
retry: 3,
backoff: {
delay: 2000
}
});
// 再使用sendCaptcha hook包装retringMethod,并通过await发送请求并获取响应结果
const result = await sendCaptcha(retringMethod, {
key: email
});
});
请求方式
@tanstack/react-query和swr提供异步函数接收请求数据,用户需要直接使用axios或fetch第三方请求库来获取数据并提供给@tanstack/react-query和swr,这样把请求的所有控制权都交给了用户,甚至可以在异步函数中模拟请求。
const fetchUsers = async () => new Promise(resolve => {
setTimeout(() => {
resolve({ data: 'your data' });
}, 1000);
});
const { data: users, isLoading, error } = useQuery('users', fetchUsers);
但alova却以Method作为代理对象,对我而言它有以下几个好处:
- 提供统一的请求方式,用户除了设置请求适配器外无需再关心用的是什么方式请求。
- 用处多样,用户可以使用Method像axios一样直接发送请求,还可以直接传入hooks中控制请求。
// 直接请求
const res = await alvoaInstance.Get('/users');
// 传入useRequest中请求
const { loading, data, error } = useRequest(alvoaInstance.Get('/users'));
-
例如使用
XMLHttpRequest请求也可以获得完整而统一的体验,包括全局的拦截器,alova提供XMLHttpRequest适配器,但在@tanstack/react-query和swr中需要自行封装。 -
易于移植到其他项目中,只需全局更换请求拦截器即可。
缓存策略
@tanstack/react-query
- 精细的缓存控制:
@tanstack/react-query允许通过配置staleTime和cacheTime来决定数据的缓存时长和过时时间。 - 自动刷新和失效:通过选项如
refetchOnWindowFocus和refetchOnReconnect配置数据在特定条件下的自动刷新。 - 缓存数据持久化:通过
persistQueryClient插件实现客户端缓存数据的持久化存储。 - 缓存调试工具:提供
ReactQueryDevtools调试工具,用于观察和处理数据请求和缓存状态。
swr
- 自定义全局缓存:SWR 使用全局缓存共享数据,同时允许通过
SWRConfig的provider选项自定义如何存储缓存。 - 修改缓存数据:提供
mutate函数来修改缓存,包括清除所有缓存数据或更新特定缓存项。 - 缓存调试工具:通过浏览器扩展查看和调试。
alova
- 多级缓存:支持L1和L2多级缓存,可在内存缓存失效后从持久化存储恢复数据,以适应不同的使用场景。
- 精细的缓存控制:允许为不同级别的缓存设置不同的过期时间,提供更灵活的缓存管理。
- 自动失效缓存:通过指定api间的关系来自动失效对应的缓存,减少手动失效的麻烦。
- 灵活的缓存存储器:可使用任意的缓存存储器存储数据,例如在客户端使用
localStorage、sessionStorage、indexedDB,react-native下的AsyncStorage,在服务端可使用多进程共享的存储器@alova/psc,甚至使用集群共享的redis存储器。
API自动化方案
由于@tanstack/react-query和swr把请求的所有控制权都交给了用户,因此API自动化方案以第三方请求工具提供,例如axios和fetch可以通过openapi-generator或@openapitools/openapi-generator-cli来自动生成请求代码。
而alova提供了更先进的openAPI解决方案。,具体包括以下:
- 同时生成 API 调用代码,以及每个 API 的 TypeScript 类型和详尽的 API 文档,即使在 JavaScript 项目中也能享受到完整的 API 类型提示,这些是
openapi-generator和@openapitools/openapi-generator-cli所不具备的。 - 全新的API集成体验,在过去,当后端开发者交付 API 给你后,你需要先打开中间的 API 文档查询并复制关键信息到你的项目里,你需要不断地在中间的 API 文档与编辑器切换,但现在,alova 的开发工具可以为你消除中间的 API 文档,像虫洞一样拉近前后端的协作距离。通过它你可以在编辑器中快速查找所需的 API 并展示这个 API 的完整文档,参照 API 参数表快速完成参数传递。
- 定时更新 api 并主动通知前端开发,不再依赖服务端开发人员通知。
这里有一个alova自动化方案的演示视频。
性能表现
依赖收集
依赖收集是指hooks内部自动跟踪组件依赖的状态,当依赖的状态改变时才会触发重新渲染视图,这更多是一种减少react组件渲染的方式。
swr和alova都支持依赖收集,而@tanstack/react-query不支持。
alova独有的性能特性
请求共享
请求共享是指多个相同的请求同时发出时只会真实发出一次请求,并将响应数据共享给这些请求,它不仅可以提升应用流畅性,还能降低服务端压力。
@tanstack/react-query和swr不支持,alova在客户端和服务端都支持。
hooks性能优化
相比@tanstack/react-query和swr的hooks,alova的场景化hooks的一个特点是,它可以针对特定场景优化性能,例如:
usePagination中可以自动预拉取上一页和下一页的列表数据,用户翻页时无需等待(秒翻)useForm中提供了表单草稿,即使刷新也不会删除未提交的数据
默认的GET缓存
由于alova的缓存key是通过 method 实例的请求信息自动计算的,在默认情况下,alova的GET具有5分钟的缓存,当多次请求重复数据时无需重复请求。
结论
经过一番深入的探讨,我们对alova、@tanstack/react-query和swr这三个请求增强库有了更全面的了解。每个库都有其独特的优势和适用场景,选择哪个库,最终取决于你的项目需求、团队的技术栈以及对性能、易用性和灵活性的考量。
- swr以其简洁性和易用性著称,如果你正在寻找一个轻量级且易于上手的解决方案,或者你的项目主要关注于快速渲染和最小化服务器请求,srw将是一个理想的选择。
- 对于需要强大的状态管理和缓存功能的应用,
@tanstack/react-query是一个很好的选择。 - 如果你不希望在API使用上花费更多时间,希望寻找一个统一的请求方案,或者正在Express、koa中发送请求,alova可能更适合你的需求。
没有最好的库,只有最适合你项目的库。花时间评估和比较不同的工具,最终选择那个能够帮助你和你的团队更高效、更愉快地工作的库就好了。
希望这篇文章能帮助你更好地理解这三个请求增强库的特点,并在实际项目中做出明智的选择。
拜拜了各位!!!