【深度对比】如何选择最适合的请求增强库

1,886 阅读11分钟

随着我们的项目越来越复杂,用户对速度和体验的要求也越来越高,现在单纯用axios或者fetch这些传统请求库来处理数据请求会产生很多额外的模板代码,已被诟病久矣,这个时候就需要使用请求增强库了。

可能有些同学会问了:“那什么是请求增强库呢?”,请求增强库一般是对传统请求库的上层封装,以hooks的形式深度绑定react、vue等UI框架,以及提供额外的性能增强特性例如响应缓存等,来达到减少模板代码、提高请求性能的目的。

而目前比较主流的请求增强库要数@tanstack/react-queryswr以及新秀alova了,它们都有不同的特点。在这里,我们来深度对比它们,你的项目更适合哪款增强工具,这样你下次遇到项目需求的时候,就能知道该如何应对了。

对比工具快速通道

咱们这就开工,从以下8个角度对比它们。

  1. 产品定位和特性
  2. hooks
  3. 框架支持
  4. 服务端支持
  5. 请求方式
  6. 缓存策略
  7. API自动化方案
  8. 性能表现

定位和特性的对比

首先是它们的定位不同。

@tanstack/react-query 主要是管理异步状态,它的强项在于能够帮你处理数据的获取、缓存和同步,你需要数据它帮你拿。数据过期了它帮你更新。

swr 侧重于异步状态管理,采用轻量级Hooks简化数据获取。

alova 提供完整的请求方案,主要帮助开发者提升API集成效率,它将开发者使用API从7个步骤降低为只需要1个步骤,帮助开发者减少大量API维护的时间。

不同的定位意味着它们在设计上会有不同的考虑,react-queryswr以提供的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-queryswr是直接以异步函数发送请求,alova需要提前创建一个请求实例,里面定义了在react下、使用fetch适配器来发送请求,此外还提供了xmlhttprequestaxios等适配器,这些都是因为它们的定位主导的设计理念,这些不同还会在后面的比较中体现。

hooks的比较

hooks数量

  • @tanstack/react-query 提供了 useQueryuseMutationuseInfiniteQuery 等 hooks。

  • swr 主要提供 useSWRuseSWRInfinite等hooks。

  • alova 提供不同请求场景下共15+请求策略,包括 useRequest, useWatcher, usePaginationuseFetcheruseForm 等。

hooks操作函数

alova 的hooks的特点之一,在于它的场景化hooks还提供了丰富的操作函数,这些函数允许开发者更精细地控制数据操作,快速实现不同复杂请求的完整的功能,而这是 react-queryswr 所没有的,例如以下是分页请求的操作函数。

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 设计的。
    1. react: @tanstack/react-query
    2. vue: @tanstack/vue-query
    3. svelte: @tanstack/svelte-query
    4. angular: @tanstack/angular-query
    5. solid: @tanstack/solid-query
  • 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-queryswr提供异步函数接收请求数据,用户需要直接使用axiosfetch第三方请求库来获取数据并提供给@tanstack/react-queryswr,这样把请求的所有控制权都交给了用户,甚至可以在异步函数中模拟请求。

const fetchUsers = async () => new Promise(resolve => {
  setTimeout(() => {
    resolve({ data: 'your data' });
  }, 1000);
});
const { data: users, isLoading, error } = useQuery('users', fetchUsers);

alova却以Method作为代理对象,对我而言它有以下几个好处:

  1. 提供统一的请求方式,用户除了设置请求适配器外无需再关心用的是什么方式请求。
  2. 用处多样,用户可以使用Method像axios一样直接发送请求,还可以直接传入hooks中控制请求。
// 直接请求
const res = await alvoaInstance.Get('/users');

// 传入useRequest中请求
const { loading, data, error } = useRequest(alvoaInstance.Get('/users'));
  1. 例如使用XMLHttpRequest请求也可以获得完整而统一的体验,包括全局的拦截器,alova提供XMLHttpRequest适配器,但在@tanstack/react-queryswr中需要自行封装。

  2. 易于移植到其他项目中,只需全局更换请求拦截器即可。

缓存策略

@tanstack/react-query

  • 精细的缓存控制@tanstack/react-query 允许通过配置staleTimecacheTime来决定数据的缓存时长和过时时间。
  • 自动刷新和失效:通过选项如refetchOnWindowFocusrefetchOnReconnect配置数据在特定条件下的自动刷新。
  • 缓存数据持久化:通过persistQueryClient插件实现客户端缓存数据的持久化存储。
  • 缓存调试工具:提供ReactQueryDevtools调试工具,用于观察和处理数据请求和缓存状态。

swr

  • 自定义全局缓存:SWR 使用全局缓存共享数据,同时允许通过SWRConfigprovider选项自定义如何存储缓存。
  • 修改缓存数据:提供mutate函数来修改缓存,包括清除所有缓存数据或更新特定缓存项。
  • 缓存调试工具:通过浏览器扩展查看和调试。

alova

  • 多级缓存:支持L1和L2多级缓存,可在内存缓存失效后从持久化存储恢复数据,以适应不同的使用场景。
  • 精细的缓存控制:允许为不同级别的缓存设置不同的过期时间,提供更灵活的缓存管理。
  • 自动失效缓存:通过指定api间的关系来自动失效对应的缓存,减少手动失效的麻烦。
  • 灵活的缓存存储器:可使用任意的缓存存储器存储数据,例如在客户端使用localStoragesessionStorageindexedDB,react-native下的AsyncStorage,在服务端可使用多进程共享的存储器@alova/psc,甚至使用集群共享的redis存储器。

API自动化方案

由于@tanstack/react-queryswr把请求的所有控制权都交给了用户,因此API自动化方案以第三方请求工具提供,例如axiosfetch可以通过openapi-generator@openapitools/openapi-generator-cli来自动生成请求代码。

而alova提供了更先进的openAPI解决方案。,具体包括以下:

  1. 同时生成 API 调用代码,以及每个 API 的 TypeScript 类型和详尽的 API 文档,即使在 JavaScript 项目中也能享受到完整的 API 类型提示,这些是openapi-generator@openapitools/openapi-generator-cli所不具备的。
  2. 全新的API集成体验,在过去,当后端开发者交付 API 给你后,你需要先打开中间的 API 文档查询并复制关键信息到你的项目里,你需要不断地在中间的 API 文档与编辑器切换,但现在,alova 的开发工具可以为你消除中间的 API 文档,像虫洞一样拉近前后端的协作距离。通过它你可以在编辑器中快速查找所需的 API 并展示这个 API 的完整文档,参照 API 参数表快速完成参数传递。

image.png

  1. 定时更新 api 并主动通知前端开发,不再依赖服务端开发人员通知。

这里有一个alova自动化方案的演示视频。

性能表现

依赖收集

依赖收集是指hooks内部自动跟踪组件依赖的状态,当依赖的状态改变时才会触发重新渲染视图,这更多是一种减少react组件渲染的方式。

swralova都支持依赖收集,而@tanstack/react-query不支持。

alova独有的性能特性

请求共享

请求共享是指多个相同的请求同时发出时只会真实发出一次请求,并将响应数据共享给这些请求,它不仅可以提升应用流畅性,还能降低服务端压力。

image.png

@tanstack/react-queryswr不支持,alova在客户端和服务端都支持。

hooks性能优化

相比@tanstack/react-queryswr的hooks,alova的场景化hooks的一个特点是,它可以针对特定场景优化性能,例如:

  1. usePagination中可以自动预拉取上一页和下一页的列表数据,用户翻页时无需等待(秒翻)
  2. useForm中提供了表单草稿,即使刷新也不会删除未提交的数据

默认的GET缓存

由于alova的缓存key是通过 method 实例的请求信息自动计算的,在默认情况下,alova的GET具有5分钟的缓存,当多次请求重复数据时无需重复请求。

结论

经过一番深入的探讨,我们对alova@tanstack/react-queryswr这三个请求增强库有了更全面的了解。每个库都有其独特的优势和适用场景,选择哪个库,最终取决于你的项目需求、团队的技术栈以及对性能、易用性和灵活性的考量。

  1. swr以其简洁性和易用性著称,如果你正在寻找一个轻量级且易于上手的解决方案,或者你的项目主要关注于快速渲染和最小化服务器请求,srw将是一个理想的选择。
  2. 对于需要强大的状态管理和缓存功能的应用,@tanstack/react-query是一个很好的选择。
  3. 如果你不希望在API使用上花费更多时间,希望寻找一个统一的请求方案,或者正在Express、koa中发送请求,alova可能更适合你的需求。

没有最好的库,只有最适合你项目的库。花时间评估和比较不同的工具,最终选择那个能够帮助你和你的团队更高效、更愉快地工作的库就好了。

希望这篇文章能帮助你更好地理解这三个请求增强库的特点,并在实际项目中做出明智的选择。

拜拜了各位!!!