likes
comments
collection
share

懒人版请求库alovajs教程4:使用useHook发送请求

作者站长头像
站长
· 阅读数 26

alova是一个轻量级的请求策略库,旨在简化接口的管理和使用。它分为两部分:

  1. 声明式实现复杂请求: 支持开发者使用声明式方法实现复杂请求,如请求共享、分页请求、表单提交、断点续传等,无需编写大量代码,提高开发效率,应用性能,减轻服务端压力。

  2. API自动管理和维护: 可生成完整的、描述详尽的请求函数的TypeScript类型,使前端项目与服务器端无缝对接,无需查阅API文档,获得类型提示。当服务端API更新时,前端项目会收到通知并阻止项目发布。

使用alovajs后,你只需要选择对应的useHook进行请求即可,被称为懒人版请求库。如果你也喜欢 alovajs,请在Github 仓库中贡献一颗 star,这对我们非常重要。

这里有很多可运行的demo

欢迎加入交流社区

有任何使用问题,你可以加入以下群聊咨询,也可以在github 仓库中发布 Discussions,如果遇到问题,也请在github 的 issues中提交,我们会在最快的时间解决。

同时也欢迎贡献你的一份力量,请移步贡献指南

以下为部分章节的教程,想学习更多 alovajs 的用法,欢迎来alova 官网学习。

alova中提供了useRequestuseWatcheruseFetcher三种 use hook 实现请求时机,由它们控制何时应该发出请求,同时将会为我们创建和维护状态化的请求相关数据,如loadingdataerror等,无需自主维护这些状态,下面我们来了解下它们。

本次我们先了解第一个 use hook,useRequest,它表示一次请求的发送,执行useRequest时默认会发送一次请求,在页面获取初始数据时是最常用的方法,同时也支持关闭它的默认的请求发送,这在提交数据等通过点击事件触发的请求场景非常有用。

初始数据请求

我们来为 todo 列表获取页面数据。

vue

<template>
  <!-- 你可以直接使用 data 来渲染 todo 列表 -->
  <div v-if="loading">Loading...</div>
  <div
    v-else-if="error"
    class="error">
    {{ error.message }}
  </div>
  <template v-else>
    <div v-for="todo in data">
      <div class="todo-title">{{ todo.title }}</div>
      <div class="todo-time">{{ todo.time }}</div>
    </div>
  </template>
</template>

<script setup>
  const {
    // loading是加载状态值,当加载时它的值为true,结束后自动更新为false
    // 它是一个Ref类型的值,你可以通过loading.value访问它,或直接绑定到界面中
    loading,

    // 响应数据,同样是Ref值
    data,

    // 请求错误对象,Ref值,请求错误时有值,否则为undefined
    error

    // 直接将Method实例传入即可发送请求
  } = useRequest(todoListGetter, {
    // 请求响应前,data的初始值
    initialData: []
  });
</script>

react

const App = () => {
  const {
    // loading是加载状态值,当加载时它的值为true,结束后自动更新为false
    // 它的值为普通的boolean值,请求状态变化时内部将自动调用set函数更新它的值
    loading,

    // 响应数据
    data,

    // 请求错误对象,请求错误时有值,否则为undefined
    error

    // 直接将Method实例传入即可发送请求
  } = useRequest(todoListGetter, {
    // 请求响应前,data的初始值
    initialData: []
  });

  // 你可以直接使用 todoList 来渲染 todo 列表
  if (loading) {
    return <div>Loading...</div>;
  } else if (error) {
    return <div class="error">{error.message}</div>;
  } else {
    return (
      <>
        <div v-for="todo in data">
          <div class="todo-title">{todo.title}</div>
          <div class="todo-time">{todo.time}</div>
        </div>
      </>
    );
  }
};

svelte

<script>
  const {
    // loading是加载状态值,当加载时它的值为true,结束后自动更新为false
    // 它是一个Writable类型的值,内部将维护它
    loading,

    // 响应数据
    data,

    // 请求错误对象,请求错误时有值,否则为undefined
    error

    // 直接将Method实例传入即可发送请求
  } = useRequest(todoListGetter, {
    // 请求响应前,data的初始值
    initialData: []
  });
</script>

<!-- 你可以直接使用 todoList 来渲染 todo 列表 -->
{#if $loading}
<div>Loading...</div>
{:else if $error}
<div class="error">{ $error.message }</div>
{:else} {#each $data as todo}
<div>
  <div class="todo-title">{ todo.title }</div>
  <div class="todo-time">{ todo.time }</div>
</div>
{/each} {/if}

绑定请求回调

如需设置请求回调,你还可以在 useRequest 的返回参数中接收回调的设置函数,如下:

const {
  // ...

  // 成功回调绑定
  onSuccess,

  // 失败回调绑定
  onError,

  // 完成回调绑定,回调在成功或失败都会调用
  onComplete
} = useRequest(todoListGetter);
onSuccess(event => {
  console.log('请求成功,响应数据为:', event.data);
  console.log('本次请求的method实例为:', event.method);
  console.log('响应数据是否来自缓存:', event.fromCache);
});
onError(event => {
  console.log('请求失败,错误信息为:', event.error);
  console.log('本次请求的method实例为:', event.method);
});
onComplete(event => {
  // event.status在成功时为success,失败时为error
  console.log('请求完成,状态为:', event.status);
  console.log('本次请求的method实例为:', event.method);
  console.log('响应数据是否来自缓存:', event.fromCache);
  if (event.data) {
    console.log('请求数据:',event.data)
  } else if (event.error) {
    console.log('错误信息:',event.error)
  }
});

onSuccess中抛出错误将会触发onError

手动发送请求

当你需要创建一条新的 todo 项时,可以先关闭默认发送请求,转为手动触发请求,并在 useRequest 中接收send函数用于手动发送请求,send函数将返回带响应数据的 Promise 实例,它将在请求响应后改为 resolve 状态。

const {
  // ...
  // 手动发送器请求的函数,调用后发送请求
  send: addTodo
} = useRequest(newTodo => alovaInstance.Post('/todo', newTodo), {
  // 当immediate为false时,默认不发出
  immediate: false
});

// 手动发送请求
const handleAddTodo = () => {
  const newTodo = {
    title: '新的todo项',
    time: new Date().toLocaleString()
  };
  // send函数返回一个Promise对象,可接收响应数据
  addTodo(newTodo)
    .then(result => {
      console.log('新增todo项成功,响应数据为:', result);
    })
    .catch(error => {
      console.log('新增todo项失败,错误信息为:', error);
    });
};

[2.9.0+]在 react 中,send 函数使用了useCallback包裹,同时它也不受闭包陷阱限制,你可以直接在事件中使用它,不用担心引起性能问题。

强制发送请求

缓存数据可以很好地提升应用流畅性和减小服务端压力,但同时也存在着数据过期的问题,当你希望穿透缓存获取最新数据时,在 use hooks 的配置中设置force属性可以帮助你。

设置静态值

force 默认为 false,设置为 true 时将每次穿透缓存,并发送请求

useRequest(alovaInstance.Get('/todo'), {
  force: true
});

动态设置 force 值

实际情况中,我们经常需要根据不同情况来设置是否需要强制发送请求,此时可以将 force 设置为一个函数,此函数可通过 send 函数传入。

const { send } = useRequest(alovaInstance.Get('/todo'), {
  force: id => {
    return !!id;
  }
});
send(1);

send 函数参数传递规则

在上面的示例中,调用 send 函数手动触发请求,它可以接受任意多个参数,这些参数将分别被以下 5 个函数接收:

在 useRequest 回调函数中接收

当 useRequest 的第一个参数设置为回调函数时可以接收到,这通常在删除列表项时很有用,具体如下:

const { send } = useRequest(id => removeTodoPoster(id));
send(1); // 上面回调函数中的id将接收到1

在 onSuccess、onError、onComplete 回调函数中接收

onSuccess、onError、onComplete 回调函数中的event.sendArgs以数组形式接收

const { send, onSuccess, onError, onComplete } = useRequest(newTodo => alovaInstance.Post('/todo', newTodo));
onSuccess(event => {
  // sendArgs的值为[1]
  console.log(event.sendArgs);
});
onError(event => {
  // sendArgs的值为[1]
  console.log(event.sendArgs);
});
onComplete(event => {
  // sendArgs的值为[1]
  console.log(event.sendArgs);
});

// 发送请求
send(1);

在 force 函数中接收

const { send } = useRequest(alovaInstance.Get('/todo'), {
  force: id => {
    return !!id;
  }
});
send(1);

设置初始响应数据

一个页面在获取到初始数据前,不可避免地需要等待服务端响应,在响应前一般需要先将状态初始化为一个空数组或空对象,以免造成页面报错,我们可以在useRequest中的第二个参数实现初始数据的设置。

// 在useRequest中设置初始数据
const {
  // 响应前data的初始值为[],而不是undefined
  // highlight-start
  data
} = useRequest(todoListGetter, {
  initialData: []
});
// highlight-end

手动修改状态值

在 alova 中,通过useRequest返回的dataloadingerror等各项状态是允许自定义修改的,这在一些情况下将变得很方便。

vue

const { data, loading, error, update } = useRequest(todoListGetter);

// ...
// 直接修改data值
data.value = {};

// 或者通过update函数修改
update({
  data: {}
});

react

在 react 中,返回的状态是直接可使用的数据,因此需通过update函数来修改。

const { data, loading, error, update } = useRequest(todoListGetter);

// ...
// 通过update修改data值
update({
  data: {}
});

svelte

在 svelte 中,useRequest返回的状态为writable类型。

const { data, loading, error, update } = useRequest(todoListGetter);

// ...
// 直接修改data值
$data = {};
// 或data.update(d => ({}));

// 或者通过update函数修改
update({
  data: {}
});

注意事项

  1. 自定义修改的值将会被useRequest内部的状态管理机制覆盖,如当你修改了data值,再次请求后data值将被赋值为最新的响应数据;
  2. 通过直接修改的状态值不会同步修改缓存数据,如需要同步修改缓存数据,建议使用updateState

:::

手动中断请求

未设置timeout参数时请求是永不超时的,如果需要手动中断请求,可以在useRequest函数被调用时接收abort方法。

const {
  // ...
  // highlight-start
  // abort函数用于中断请求
  abort
  // highlight-end
} = useRequest(todoListGetter);

// highlight-start
// 调用abort即可中断请求
const handleCancel = () => {
  abort();
};
// highlight-end

[2.9.0+]在 react 中,abort 函数使用了useCallback包裹,同时它也不受闭包陷阱限制,你可以直接在事件中使用它,不用担心引起性能问题。

[2.6.2+]另外,这个abort函数也会同时绑定到当前的 method 实例上,因此你也可以这样来中断请求。

useRequest(todoListGetter);

// highlight-start
// 调用method上的abort也可以中断当前请求
const handleCancel = () => {
  todoListGetter.abort();
};
// highlight-end

[2.6.2+]你还可以在beforeRequest中调用abort中断请求。

const alovaInst = createAlova({
  // ...
  beforeRequest(method) {
    if (someCondition) {
      method.abort();
    }
  }
});

为什么要使用alova

alova 也致力于解决客户端网络请求的问题,但与其他请求库不同的是,alova 选择了业务场景化请求策略的方向,它配合axios/fetch api等请求库后能满足你绝大部分请求需求(99%)的同时,还提供了丰富的高级功能。

  • 你可能曾经也在思考着应该封装fetchaxios,现在你不再需要这么做了,通过 alova 使用声明的方式完成请求,例如请求共享、分页请求、表单提交、断点上传等各种较复杂的请求,以及自动化缓存管理、请求共享、跨组件更新状态等。
  • alova 是轻量级的,只有 4kb+,是 axios 的 30%+。
  • 目前支持vue/react/react-native/svelte,以及next/nuxt/sveltekit等 SSR 框架,同时也支持Uniapp/Taro多端统一框架。
  • alova 是低耦合的,你可以通过不同的适配器让 alova 在任何 js 环境下,与任何 UI 框架协作使用(内置支持的 UI 框架为vue/react/svelte),并且提供了统一的使用体验和完美的代码迁移。
  • 使用 alova 还能实现 api 代码的高聚合组织方式,每个 api 的请求参数、缓存行为、响应数据转换等都将聚集在相同的代码块中,这对于管理大量的 api 有很大的优势。

与其他请求库的对比

多框架支持

现在,你还可以在 vue options(vue2 和 vue3) 写法中完美使用 alova,点此查看详情。后续我们将陆续支持以下框架:

  • 函数式,如solid/preact/qwik
  • class 式,如angular/lit/stencil
  • options 式,如原生小程序(中国🇨🇳)

alovajs 还提供了其他强大的请求策略:

名称描述文档
分页请求策略自动管理分页数据,数据预加载,减少不必要的数据刷新,流畅性提高 300%,编码难度降低 50%usePagination
无感数据交互策略全新的交互体验,提交即响应,大幅降低网络波动造成的影响,让你的应用在网络不稳定,甚至断网状态下依然可用useSQRequest
表单提交策略为表单提交而设计的 hook,通过此 hook 你可以很方便地实现表单草稿、多页面(多步骤)表单,除此以外还提供了表单重置等常用功能useForm
文件上传策略更简单的文件上传策略,支持对 base64、Blob、ArrayBuffer、Canvas 数据的自动识别和转换useUploader
发送验证码验证码发送 hook,减掉你在开发验证码发送功能时的繁琐。useCaptcha
自动重新拉取数据在一定条件下自动重新拉取数据,保证始终展示最新数据。useAutoRequest
跨组件触发请求一个 alova 中间件,消除组件层级的限制,在任意组件中快速地触发任意请求的操作函数actionDelegationMiddleware
串行请求的 useRequestalova 的串行请求方式更加简洁易用的串行请求 use hook,提供统一的 loading 状态、error、回调函数useSerialRequest
串行请求的 useWatcheralova 的串行请求方式更加简洁易用的串行请求 use hook,提供统一的 loading 状态、error、回调函数。useSerialWatcher
请求重试策略请求失败自动重试,它在重要的请求和轮询请求上发挥重要作用useRetriableRequest
SSE 请求通过 Server-sent Events 进行请求useSSE

想学习更多 alovajs 的用法,欢迎来alova 官网学习。如果你也喜欢 alovajs,请在Github 仓库中贡献一颗 star,这对我们非常重要。

如果觉得文章对你有帮助,请别吝啬你的赞和评论哈,说说你对 alovajs 怎么看的,或者可以问一些问题,我会尽量回答的,你的支持是我创作的最大动力!哈哈哈哈哈哈~

欢迎加入交流社区

有任何问题,你可以加入以下群聊咨询,也可以在github 仓库中发布 Discussions,如果遇到问题,也请在github 的 issues中提交,我们会在最快的时间解决。

同时也欢迎贡献你的一份力量,请移步贡献指南

往期文章