V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
ScottHU
V2EX  ›  程序员

是时候该换掉你的 axios 了

  •  
  •   ScottHU · 330 天前 · 17312 次点击
    这是一个创建于 330 天前的主题,其中的信息可能已经有所发展或是发生改变。

    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 中你可以默认享受到这样的待遇,以下展示下效果

    screenshots.gif

    请求共享

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

    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%,以下是官方提供的示例,有兴趣的同学可以去看看。

    分页列表示例

    下拉加载示例

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

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

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

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

    screenshots.gif

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

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

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

    数据预拉取

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

    写在最后

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

    alova 官网

    alova 的 Github 地址

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

    125 条回复    2023-10-07 17:55:41 +08:00
    1  2  
    Masoud2023
        101
    Masoud2023  
       329 天前
    alova 这个 API 设计的蛮漂亮的,结合 React 感觉确实不错
    lujiaxing
        102
    lujiaxing  
       329 天前
    用 axios 很大程度上是为了兼容 IE11, Chrome 49 等相对比较老的浏览器.
    如果不考虑它们的话, 其实直接用 FETCH API 都完全没问题...
    dufu1991
        103
    dufu1991  
       329 天前
    不管它好不好,反正我是暂时用着 axios ,等 fetch 兼容性起来,直接跳过去。
    jqtmviyu
        104
    jqtmviyu  
       329 天前
    11K 还大, 我和服务器和用户缺这几 K 吗?
    请求库不绑定框架, 是优点好吧, 我可不想换一个框架熟悉一套 api.
    简单场景我封装下 fetch 就够了
    wwatson
        105
    wwatson  
       329 天前
    怀疑你是故意来招黑的
    weixiangzhe
        106
    weixiangzhe  
       329 天前
    东西是不错的,但推广点应该还是要对标到 ahook 的 useRequest ,axios 的简化功能一句带过就好啦
    weixiangzhe
        107
    weixiangzhe  
       329 天前 via Android
    不过我还是选择那个 use-request 包 加 ky
    xingyuc
        108
    xingyuc  
       329 天前
    我还以为是这种 https://angular.cn/api/common/http/HttpClient ,使用类似 Observable 的类型,刚想说学不会
    yanbinkwan96
        109
    yanbinkwan96  
       329 天前
    @fan88 #44 我也是服了,用 jq 的就这素质?
    l4ever
        110
    l4ever  
       329 天前
    等生态更新吧, 目前用 openapi-typescript-codegen 生成的, 他用你推荐的 alova 我就更新一下咯
    他不用我就懒得更新了.
    shenqi
        111
    shenqi  
       329 天前
    你说得不错,但是你为什么要在方法名首字母大写?
    gunnarli
        112
    gunnarli  
       329 天前
    换好麻烦啊。
    Charod
        113
    Charod  
       329 天前
    已阅,继续 fetch 🐶
    encro
        114
    encro  
       329 天前   ❤️ 1
    等你实现拦截器,预定义配置,兼容性之类的,发现代码一样肿了。
    obulks
        115
    obulks  
       329 天前
    11+kb 还臃肿,没地方黑了是吧
    zhoupeng199
        116
    zhoupeng199  
       329 天前
    发掘金被骂了,发这还被骂
    witcat
        117
    witcat  
       329 天前
    没必要和 React 结合啊,结合了两边又不一定做得好,请求不一定有 axios 可靠,hook 不一定有 swr 科学。
    当然这只是我猜的,我并没有深入了解,如果两面都能兼顾,那一定会非常受欢迎。
    然鹅,不管是 axios/fetch 与 swr/react query 等等结合,只要有文档和配置,openapi generator 一类的工具都可以一键生成。
    ajan
        118
    ajan  
       329 天前
    看到标题,忍不住进来恶心一下...
    fan88
        119
    fan88  
       329 天前
    @yanbinkwan96 是啊,前端工程化一出来耽误老子捞钱啊,本来 JQ 一把梭,网站 F12 扒源码,做个项目嘎嘎方便钱嘎嘎挣。 现在搞的烦的一批。
    Dragonphy
        120
    Dragonphy  
       329 天前
    不如用 fetch
    lete
        121
    lete  
       329 天前 via Android
    掘金发,这也发,标题党你是真找喷啊
    nuxio
        122
    nuxio  
       328 天前
    不好意思 我还是选择 axios ,看你们发帖的内容不爽,耐着性子看你们文档更不爽,一个请求库,你造这么多词干什么?还模型,模你个大头鬼。
    完全没有想要用的欲望,看到都恶心。
    xzjs
        123
    xzjs  
       328 天前
    等等,说的这几个优点我怎么感觉反而是缺点了,我修改完更新列表,你给我缓存了,我如果要缓存就一个变量的事,不需要 alova 替我做这事啊
    paradox8599
        124
    paradox8599  
       328 天前 via Android
    这不是 swr ?
    huwenzhe
        125
    huwenzhe  
       215 天前
    @GzhiYi

    Weekly Downloads: 2385
    Github stars: 1.8k
    1  2  
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1075 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 35ms · UTC 22:41 · PVG 06:41 · LAX 15:41 · JFK 18:41
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.