是时候该换掉你的 axios 了

2023-06-14 12:05:53 +08:00
 ScottHU

axios 是一个基于 Promise 的 HTTP 客户端,每周的 npm 下载量 4000W+,如果回到在 10 年前,promise 式的请求工具是一个很大的创新,它解决了请求繁琐的问题,在那个性能要求不那么高的年代可谓是一骑绝尘。但随着时间的推移,Axios 在开发效率和性能方面开始有所落后,现在都已经是 2023 年了,面对日益复杂的需求,我们需要的是一款更具创新性和领先性的请求工具,而 promise 式的请求工具只能被称为传统了,如果你想保持在快速发展的前沿,那么请继续阅读。

首先我想声明的是,我确实不是标题党,接下来我将通过暴露随着时间的推移,axios 在一些方面表现的力不从心,并推荐一个新的,相比 axios 更具现代化和创新性的请求工具给你,它就是 轻量级的请求策略库 alova

接下来我们看看 Promise 式请求工具的弱点( axios )

1. 与 React 、Vue 等框架割裂

现在,React 、Vue 等前端 UI 框架对于前端来说几乎是不可缺少的,axios 无法和这些框架的状态深度绑定,需要开发者自行维护它们,导致开发效率较低。

2. 在性能方面毫无作为

2023 年了,相比 10 年前的应用已经复杂了不知几个数量级,在请求方面要求也越来越高,来保证页面性能的要求,axios 在这方面毫无作为,例如在频繁地重复请求、同时发起多个相同请求等场景。

3. 臃肿的体积

根据 bundlephobia 显示,axios 的体积在压缩状态下有 11+kb ,不信的话,你可以点此去查看

4. 响应数据的 Ts 类型定义混乱

在使用 axios 时,你可能经常会这样写:

// 创建一个 axios 实例
const inst = axios.create({
  baseURL: 'https://example.com/'
})

// 在响应拦截器中返回 data
inst.interceptors.response.use(response => {
  if (response.status === 200) {
    return response.data
  }
  throw new Error(response.status)
})

interface Resp {
  id: number
}
inst.get<Resp>('/xxx').then(result => {
  // result 的类型总是为 axios.AxiosResponse<Resp>
  data.data
})

不知道是 axios 故意为之还是忽略了,以上的发起的 GET 请求中,响应数据result的类型总是axios.AxiosResponse<Resp>的,但其实我们在响应拦截器中已经将response.data返回了,这导致响应数据类型混乱而被困扰。

在 alova 中是如何解决的呢?

alova 作为一个更加现代化,更加适应复杂应用的请求方案,也给出了它更加优雅的解决方案。同时为了降低给的学习成本,也保持了和 axios 相似的 api 设计,看起来就很熟悉有木有。

alova 读作“阿洛娃”,虽然和 axios 一样都是以 a 开头,以下两个名称需要注意区分哦!

与 UI 框架深度融合,自动管理请求相关数据

假设我们需要发起一个基本的数据获取请求,以 vue 为例,直接上对比代码。

axios

<template>
  <div v-if="loading">Loading...</div>
  <div v-else-if="error" class="error">
    {{ error.message }}
  </div>
  <div v-else>{{ data }}</div>
</template>

<script setup>
import axios from 'axios';
import { ref, onMounted } from 'vue';

const loading = ref(false);
const error = ref(null);
const data = ref(null);

const requestData = () => {
  loading.value = true;
  axios.get('http://xxx/index').then(result => {
    data.value = result;
  }).catch(e => {
    error.value = e;
  }).finally(() => {
    loading.value = false;
  });
}
onMounted(requestData);
</script>

alova

<template>
  <div v-if="loading">Loading...</div>
  <div v-else-if="error" class="error">
    {{ error.message }}
  </div>
  <div v-else>{{ data }}</div>
</template>

<script setup>
import { createAlova } from 'alova';

const pageData = createAlova({ baseURL: 'http://xxx' }).Get('/index');
const { loading, data, error } = useRequest(pageData);
</script>

在 axios 中需要自己创建对应的请求状态并自行维护,而 alova 却帮你接管了这项工作

开箱即用的高性能功能

传统 Promise 式的请求工具主要定位于通过 Promise 的方式简化请求,而提高性能可能是它们最不会考虑的一点,但作为请求策略库的 alova 中却着重突出这一点,在 alova 中默认开启了内存缓存和请求共享,这两项可以极大地提高请求性能,提升用户体验的同时还能降低服务端压力,让我们来一一了解下它们吧。

内存缓存

内存模式就是在请求响应后将响应数据保存在本地内存中,当下次再发起相同请求时就会使用缓存数据,而不会再次发送请求,试想一下,当你在实现一个列表页,点击列表项可以进入详情页查看数据,你会想到用户可能会频繁在列表中点击查看详情,当详情数据没有变化时,如果每一次进入详情页都要请求一次未免也太浪费了,而且每次还需要用户等待加载。在 alova 中你可以默认享受到这样的待遇,以下展示下效果

请求共享

你可能遇到过这种情况,当一个请求发出但还未响应时,又发起了相同请求,就造成了请求浪费,或者重复提交问题,例如以下三种场景:

  1. 一个组件在创建时会获取初始化数据,当一个页面同时渲染多个此组件时,将会同时发出多次相同请求;
  2. 提交按钮未被禁用,用户点击了多次提交按钮;
  3. 当预加载还未完成时进入了预加载页面,将会发起多次相同请求;

共享请求就是用来解决这些问题的,它是通过复用请求的方式来实现的,由于这种案例无法直观展示,就不展示了,有兴趣的小伙伴可以自行体验体验。

除此以外,自称是请求策略库的 alova 还提供了特定场景下的请求策略,我们将在下文中介绍,有兴趣的小伙伴请继续往下看。

轻量级的体积

压缩状态下的 alova 只有 4kb+,只有 axios 的 30%+,不信的话,你可以点此去查看

更加直观的响应数据 TS 类型

在 axios 中,你想要定义响应数据的类型真是会让人感到困惑,如果你是个 Typescript 的重度用户,alova 可以给你提供完整的类型体验,当你在请求处定义响应数据时的类型后,你可以在多处享受到它,会让你感觉很清晰,我们来看看。

interface Resp {
  id: number
}
const pageData = createAlova({ baseURL: 'http://xxx' }).Get<Resp>('/index');
const {
  data,  // data 的类型为 Resp
  loading, error, onSuccess, send
} = useRequest(pageData);
onSuccess(event => {
  // 在成功回调中获取响应数据时,event.data 的值类型也是 Resp
  console.log(event.data);
});

const handleClick = async () => {
  // send 函数可以手动再次发送请求,它将可以接收到响应数据,它的值类型还是 Resp
  const data = await send();
}

至此,相比传统的 Promise 式请求库,你可能已经初步了解了 alova 的厉害。

但... 它的特性还远不止于此!

alova 的其他特性

多 UI 框架同时支持

alova 同时支持 react 、vue 、svelte ,无论你使用哪种 UI 框架,它都能满足你。

与 axios 相似的 api 设计,用起来更简单熟悉

alova 的请求信息构造几乎和 axios 相同,我们来对比一下它们的 GET 和 POST 请求。

GET 请求

// axios
axios.get('/index', {
  // 设置请求头
  headers: {
    'Content-Type': 'application/json;charset=UTF-8'
  },
  // params 参数
  params: {
    userId: 1
  }
});

// alova
const todoListGetter = alovaInstance.Get('/index', {
  // 设置请求头
  headers: {
    'Content-Type': 'application/json;charset=UTF-8'
  },
  // params 参数
  params: {
    userId: 1
  }
});

POST 请求

// axios
axios.post('/login', {
  username: 'xxx',
  password: 'ppp'
}, {
  // 设置请求头
  headers: {
    'Content-Type': 'application/json;charset=UTF-8'
  },
  // params 参数
  params: {
    userId: 1
  }
});

// alova
const loginPoster = alovaInstance.Post('/login', {
  username: 'xxx',
  password: 'ppp'
}, {
  // 设置请求头
  headers: {
    'Content-Type': 'application/json;charset=UTF-8'
  },
  // params 参数
  params: {
    userId: 1
  }
});

(请求策略)高性能分页请求策略

自动维护分页相关数据和状态,并提供了常用的分页数据操作能力,据官方介绍,可以让列表页流畅性提高 300%,编码难度降低 50%,以下是官方提供的示例,有兴趣的同学可以去看看。

分页列表示例

下拉加载示例

(请求策略)无感数据交互

这个在我看来,这个无感数据交互请求策略可谓是一大创举,我把它理解为更加可靠的乐观更新,官网是这样解释的:

无感数据交互是指用户在与应用进行交互时,无需等待即可立即展示相关内容,或者提交信息时也无需等待即可展示操作结果,就像和本地数据交互一样,从而大幅提升应用的流畅性,它让用户感知不到数据传输带来的卡顿。可以更高限度地降低网络波动带来的问题,你的应用在高延迟网络甚至是断网状态下依然可用。

在我的体验过程中,即使在弱网状态下,也可以让我感受到一种毫无延迟带来的顺畅感,你也来感受下吧。

据我了解,它使用以下技术:

  1. 持久化的请求队列来保证请求的安全性和串联性;
  2. 请求重试策略机制,来保证请求的顺利完成;
  3. 虚拟响应数据(一个创新的概念),来作为未响应时的数据占位,以便在响应后定位它并替换为实际数据。

关于无感数据交互更具体的可以在官网了解哦

数据预拉取

通过拉取数据的方式预先加载好数据并缓存在本地,当真正用到这部分数据时就可以命中缓存并直接显示数据,这种方式也极大地提升了用户体验。

写在最后

总之,alova 作为一个新生代的请求工具,具有很大的潜力,你也想试用的话,可以点击以下链接去了解。

alova 官网

alova 的 Github 地址

写作不易,看都看到这了,不如帮我点个免费的爱心吧!!!感谢你的喜欢

18559 次点击
所在节点    程序员
125 条回复
wusheng0
2023-06-14 13:34:35 +08:00
@wusheng0
>
https://juejin.cn/post/7213923957824979000

JOU_alova2
没想到我写的库居然冲上了热榜,但我好像来迟了点,非常感谢古韵对我的支持,他也是我去年邀请的第一批体验用户之一。
评论我都自习看了下,在这里为大家稍微解答下,首先,文章标题确实有点过了,alova 和 axios 是一种互相协作的关系而没有替代一说,在 RSM 模型中,请求发送照样需要使用 axiosfetch 等,而且如果 alova+axios 的话,还是可以享受到 axios 的全部功能的,这点可以在 axios 适配器文档中看到。

然后第二个大家关心的问题是和 reactquery 和 swr 区别不大,在这里我想说的是,它们所解决的问题是一样的。但解决方案不一样,alova 和 reactquery/swr 最大区别是,alova 中抽象出了 Method 的概念,Method 表示以怎样的方式外理请求,以如请求相关参数,请求超时时间,是否使用缓存还是发出请求,是否使用上传下载进度等等,都是和请求相关的信息,然后达到优化了请求信息的管理方式的效果,也达到了在不同请求环培下统一的请求方式,你可以不关心到底是 axiosfetch 还是 taro.reauest 在发送请求,上层使用都一样,还有一个好外是 Method 实例是贯穿请求和响应的,对干管理缓存也好,响应的状态也好,你都不需要自己去设詈 kev ,因为 Method 就是最好的 kev ,useReauestuseWatcheruseFetcher 代表请求时机,即表示在何时发出请求不知道有没有解答大家的疑问,在这里也感谢大家对 alova 展开的热议 @稀土掘金技术社区
agdhole
2023-06-14 13:36:21 +08:00
重新发明 swr ?
siweipancc
2023-06-14 13:38:07 +08:00
害怕,你们就是这么迭代“工具”的?把前人变得一文不值
veike
2023-06-14 13:38:42 +08:00
一直用的 fetch ,自己封装了一下,好多项目都用的自己写的 fetch.js
huangqihong
2023-06-14 13:54:11 +08:00
在掘金看过这篇文章,标题也是这个,我就想不通,在掘金多说了标题过了,为什么还会在 v2 也用这个标题呢
老老实实推荐个工具不行吗?
是时候该换掉你的 标题 了
diagnostics
2023-06-14 13:59:33 +08:00
前端圈真会“玩”
adminisqq
2023-06-14 14:02:21 +08:00
前端娱乐圈
gps949
2023-06-14 14:02:47 +08:00
看到标题进来前还以为要说 fetch 呢……
likunyan
2023-06-14 14:05:42 +08:00
重新发明 swr ?
wtf12138
2023-06-14 14:05:47 +08:00
很早之前在掘金上看过了,标题都不带改的
不愧是前端娱乐圈
Mark85
2023-06-14 14:05:51 +08:00
https://www.attojs.com/api/#data

https://ahooks.js.org/hooks/use-request/index

除了体积外,你说的其他缺点不就引个 hooks 就解决的事么?
weijancc
2023-06-14 14:16:35 +08:00
跟 starrocks 一样, 宣传自己的东西就先踩一下别人, 只能说真下头, 狗都不用.
supertan
2023-06-14 14:16:40 +08:00
@NessajCN #15 这是一个业务关联的,如果你的数据不是一直在变化(或者实时要求不是立即更新),比如历史表、班级成员之类的,那么相同上送项查回来的数据就是一样的,这种完全就可以缓存( 10 秒、60 秒)减少服务端压力+优化体验。 这有啥好杠的?
BUHeF254Lpd1MH06
2023-06-14 14:25:17 +08:00
这个对比感觉就像是公司里,用了很多资源造了一个东西,然后领导非得问你“到底进步了多少?”。然后你经过仔细研究数据对比,认为 11kb 的 axios 太臃肿了,我的提升了 50%达到了竟然的 4kb
GzhiYi
2023-06-14 14:35:44 +08:00
Weekly Downloads: 747
Github stars: 1.2k
ShundL
2023-06-14 14:37:05 +08:00
你们前端又出新玩意儿啦?
Uyloal
2023-06-14 14:37:41 +08:00
我一般 fetch 一把梭😆
GzhiYi
2023-06-14 14:38:21 +08:00
@NessajCN 我也觉得这个功能比较扯,尽管可能该库有对错误或者数据差异做一些异常抛出(我没细心看),但这样可能存在致命问题(缓存数据与数据库数据不一致引起后续判断 /操作错误)的功能也会出现,谁敢用在生产环境。所以我觉得为了安全起见,至少移除这样的功能。
summerLast
2023-06-14 14:45:38 +08:00
myon
2023-06-14 14:46:20 +08:00
掘金没被骂够换个地方继续找骂是吧

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/948621

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX