在 React 中,ahooks 的 useRequest 是一个非常强大的异步请求管理工具,它不仅能够帮助开发者轻松处理网络请求,还提供了高级功能,如轮询、节流、防抖、缓存等。在 Vue 中,我们可以通过 Vue 3 的组合式 API 实现类似的功能,管理异步请求及其状态。
本文将结合 Vue 3 的 reactive、ref 和 watchEffect 等功能,介绍如何在 Vue 中实现类似 useRequest 的异步请求 Hook,并展示如何实现一些常见的功能,如轮询、防抖、节流和缓存等。
1. Vue 组合式 API 概述
Vue 3 的组合式 API 提供了 reactive、ref、computed 和 watchEffect 等功能,使得我们可以以一种更灵活、模块化的方式构建功能。在这个基础上,我们可以封装一个类似 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 的核心,它封装了异步请求的发起过程,并管理请求的 loading、data 和 error 状态。
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,
};
}
在这里,debouncedRun 和 throttledRun 分别通过 lodash 的 debounce 和 throttle 实现。如果用户提供了 debounceWait 或 throttleWait 参数,我们就使用对应的封装函数来管理请求的频率。
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,我们可以轻松地实现类似 ahooks 的 useRequest,并为其扩展多种高级功能,如轮询、防抖、节流和缓存。这种模式不仅简化了异步请求的管理,还增强了代码的可维护性和扩展性。
随着项目的复杂度增加,合理封装异步请求逻辑,管理状态和副作用,能够大大提升开发效率。如果你习惯了 React 中的 useRequest,那么在 Vue 中实现类似的 Hook 将为你带来更好的开发体验。
通过本文,我们展示了如何在 Vue 3 中构建一个强大的异步请求管理工具,希望能为你的项目提供参考和帮助。