在 Vue 中实现类似 ahooks useRequest 的异步请求 Hook

在 React 中,ahooksuseRequest 是一个非常强大的异步请求管理工具,它不仅能够帮助开发者轻松处理网络请求,还提供了高级功能,如轮询、节流、防抖、缓存等。在 Vue 中,我们可以通过 Vue 3 的组合式 API 实现类似的功能,管理异步请求及其状态。

本文将结合 Vue 3 的 reactiverefwatchEffect 等功能,介绍如何在 Vue 中实现类似 useRequest 的异步请求 Hook,并展示如何实现一些常见的功能,如轮询、防抖、节流和缓存等。

1. Vue 组合式 API 概述

Vue 3 的组合式 API 提供了 reactiverefcomputedwatchEffect 等功能,使得我们可以以一种更灵活、模块化的方式构建功能。在这个基础上,我们可以封装一个类似 useRequest 的 Hook,帮助我们更好地管理异步请求的状态。

2. useRequest 在 Vue 中的核心实现

在 Vue 中,我们可以通过以下几个步骤来实现 useRequest

2.1 基本结构

我们首先封装一个基础的 useRequest 函数,用来管理请求状态的更新。它会接收一个异步函数 service 以及可选的配置项 options

import { reactive, ref } from 'vue';

export function useRequest(service, options = {}) {
  const state = reactive({
    data: null,
    error: null,
    loading: false,
  });

  const run = async (...args) => {
    state.loading = true;
    state.error = null;
    try {
      const result = await service(...args);
      state.data = result;
      state.loading = false;
      return result;
    } catch (err) {
      state.error = err;
      state.loading = false;
      throw err;
    }
  };

  return {
    ...state,
    run,
  };
}

2.2 参数说明

  • service:这是一个异步函数,用于发起实际的请求。
  • options:配置项,用于扩展功能,比如节流、防抖、缓存等。

2.3 run 函数

run 函数是 useRequest 的核心,它封装了异步请求的发起过程,并管理请求的 loadingdataerror 状态。

3. 实现高级功能

接下来,我们扩展 useRequest,实现一些常用的高级功能,如轮询、防抖、节流和缓存等。

3.1 轮询

为了实现轮询功能,我们可以在每次请求完成后,根据设定的间隔时间再次发起请求。我们通过 setInterval 来实现这个功能。

import { onUnmounted } from 'vue';

export function useRequest(service, options = {}) {
  const { pollingInterval } = options;
  const state = reactive({
    data: null,
    error: null,
    loading: false,
  });

  let timer = null;

  const run = async (...args) => {
    state.loading = true;
    state.error = null;
    try {
      const result = await service(...args);
      state.data = result;
      state.loading = false;

      if (pollingInterval) {
        timer = setTimeout(() => run(...args), pollingInterval);
      }

      return result;
    } catch (err) {
      state.error = err;
      state.loading = false;
      throw err;
    }
  };

  onUnmounted(() => {
    if (timer) clearTimeout(timer);
  });

  return {
    ...state,
    run,
  };
}

通过设置 pollingInterval,我们可以在请求完成后指定的时间间隔内再次触发请求,并通过 onUnmounted 钩子在组件卸载时清理定时器,避免内存泄漏。

3.2 防抖与节流

防抖和节流是前端开发中常见的功能,尤其在处理频繁请求时非常有用。我们可以使用 lodash 或者自己实现的防抖和节流函数来封装 run

import { debounce, throttle } from 'lodash';

export function useRequest(service, options = {}) {
  const { debounceWait, throttleWait } = options;
  const state = reactive({
    data: null,
    error: null,
    loading: false,
  });

  const run = async (...args) => {
    state.loading = true;
    state.error = null;
    try {
      const result = await service(...args);
      state.data = result;
      state.loading = false;
      return result;
    } catch (err) {
      state.error = err;
      state.loading = false;
      throw err;
    }
  };

  const debouncedRun = debounceWait ? debounce(run, debounceWait) : null;
  const throttledRun = throttleWait ? throttle(run, throttleWait) : null;

  return {
    ...state,
    run: debouncedRun || throttledRun || run,
  };
}

在这里,debouncedRunthrottledRun 分别通过 lodashdebouncethrottle 实现。如果用户提供了 debounceWaitthrottleWait 参数,我们就使用对应的封装函数来管理请求的频率。

3.3 请求缓存

缓存可以避免重复发起相同的请求,从而提高性能。我们可以通过一个简单的 Map 来存储缓存的结果。

const cache = new Map();

export function useRequest(service, options = {}) {
  const { cacheKey } = options;
  const state = reactive({
    data: null,
    error: null,
    loading: false,
  });

  const run = async (...args) => {
    if (cacheKey && cache.has(cacheKey)) {
      state.data = cache.get(cacheKey);
      return cache.get(cacheKey);
    }

    state.loading = true;
    state.error = null;
    try {
      const result = await service(...args);
      state.data = result;
      cacheKey && cache.set(cacheKey, result);
      state.loading = false;
      return result;
    } catch (err) {
      state.error = err;
      state.loading = false;
      throw err;
    }
  };

  return {
    ...state,
    run,
  };
}

通过 cacheKey 参数,我们可以将请求结果存储到 Map 中,并在后续相同请求中直接从缓存中获取结果。

4. 在 Vue 项目中的使用示例

现在我们已经实现了一个功能丰富的 useRequest,接下来展示它在实际 Vue 项目中的使用。

<template>
  <div>
    <div v-if="loading">Loading...</div>
    <div v-if="error">Error: {{ error.message }}</div>
    <div v-if="data">User: {{ data.name }}</div>
    <button @click="run">Reload</button>
  </div>
</template>

<script>
import { useRequest } from './useRequest'; // 引入我们封装的 useRequest

export default {
  setup() {
    const getUserInfo = () => fetch('/api/user').then(res => res.json());
    const { data, error, loading, run } = useRequest(getUserInfo, {
      pollingInterval: 5000, // 每5秒轮询一次
    });

    return {
      data,
      error,
      loading,
      run,
    };
  },
};
</script>

在这个示例中,我们使用 useRequest 来发起一个用户信息请求,并启用了 5 秒的轮询功能。我们还展示了如何手动重新加载数据。

5. 总结

通过 Vue 3 的组合式 API,我们可以轻松地实现类似 ahooksuseRequest,并为其扩展多种高级功能,如轮询、防抖、节流和缓存。这种模式不仅简化了异步请求的管理,还增强了代码的可维护性和扩展性。

随着项目的复杂度增加,合理封装异步请求逻辑,管理状态和副作用,能够大大提升开发效率。如果你习惯了 React 中的 useRequest,那么在 Vue 中实现类似的 Hook 将为你带来更好的开发体验。

通过本文,我们展示了如何在 Vue 3 中构建一个强大的异步请求管理工具,希望能为你的项目提供参考和帮助。

原文链接:https://juejin.cn/post/7416567316999553033 作者:lucifer311

(0)
上一篇 2024年8月28日 上午10:03

相关推荐

发表回复

登录后才能评论