Vue手动实现useRequest
功能介绍
useRequest 是个网络请求Hooks,可以解决Vue3开发过程中,常见的网络请求场景,核心功能如下:
- 👨🍳 状态获取
- 💦 节流
- 🫨 防抖
- ☎️ 依赖刷新
// 基本用法 const { data, loading, run } = useRequest(asyncFn);
相关参数
参数名 | 类型 | 默认值 | 描述 |
manual |
boolean
|
true | 设置该参数为true,useRequest不会默认执行,需要通过run 来触发执行 |
throttleWait
|
number | undefined | 设置该参数(毫秒),若频繁触发run 方法,则会以节流策略进行请求 |
debounceWait
|
number | undefined | 设置该参数(毫秒),若频繁触发run 方法,则会以防抖策略进行请求 |
refreshDeps
|
function | undefined | 设置该参数,在依赖变化时, 会自动调用run 方法,实现刷新的效果 |
defaultValue
|
any | undefined | data的初始化默认值 |
clearPrevValue
|
boolean | false | 设置该参数为true,执行run 方法时,会将data的值恢复为默认值 |
Typescript 类型定义
import { watch } from "vue"; import type { Ref } from "vue"; /** * @name TypeUseRequestAsyncFn 第一个参数 传递的异步函数 */ export type TypeUseRequestAsyncFn<T = any> = (...args: any[]) => Promise<T>; /** * @name TypeUseRequestParameters 第二个参数 相关配置 */ export type TypeUseRequestParameters = Partial<{ /** * @param manual 初始化请求一次 * @default false */ manual: boolean; /** * @param throttleWait 节流执行时间(毫秒) * @description 传递值后生效 */ throttleWait: number; /** * @param debounceWait 防抖执行时间(毫秒) * @description 传递值后生效 */ debounceWait: number; /** * @param clearPrevValue 清空上一轮值为默认值 * @default undefined * @description 若执行run方法,则立即清空data的值为默认值(defaultValue) */ clearPrevValue: boolean; /** * @param defaultValue 默认值 * @default undefined * @description 若执行run方法,则立即清空data的值为默认值(defaultValue) */ defaultValue: any; /** * @name refreshDeps 监听参数,调用run方法 * @description 跟Vue3 watch API用法完全一致 */ refreshDeps: typeof watch; }>; type TypeUseRequest< T extends TypeUseRequestAsyncFn = TypeUseRequestAsyncFn, R = Awaited<ReturnType<T>>, > = (fn: R) => { data: Ref<R>; run: () => Promise<R>; loading: Ref<boolean>; }; async function getList() { return [1, true, "2"]; } // 调用useRequest方法,得到的结果 完整的Typescript结果类型 type Returns = TypeUseRequest<typeof getList>;
基本功能
🤖 useRequest
基本功能,包括:
- 💁♂️ manual 初始化是否执行run方法
- 💁♂️ defaultValue 默认参数
- 💁♂️ clearPrevValue 执行新的请求执行前,将data的值恢复为默认值
- 🙆♂️ return
data
、loading
、run
参数,供业务组件使用
import { onMounted, readonly, ref } from "vue"; import type { TypeUseRequestAsyncFn, TypeUseRequestParameters } from "./interface"; /** * @name useRequest 🚀 网络请求Hooks */ function useRequest<T extends TypeUseRequestAsyncFn, R = Awaited<ReturnType<T>>>( fn: T, params?: TypeUseRequestParameters ) { const defaultValue = readonly(params?.defaultValue); const loading = ref(false); const data = ref<R>(defaultValue); async function run() { try { // 是否清空上一轮值 if (params?.clearPrevValue) { data.value = defaultValue; } loading.value = true; // 更新loading状态 const res = await fn(); data.value = res; return res as R } catch (error) { // 错误埋点 console.log("@-error", error); } finally { loading.value = false; } } onMounted(() => { // 初始化是否执行 params?.manual || run(); }); return { run, data, loading, }; };
防抖与节流
防抖与节流作为请求场景中最常见的需求,实现起来也非常简单,不需要引入任何三方库就可以快速实现。
🧑💻 以下是手动实现 throttle debounce 的方法,直接上代码:
/** * @name throttle 节流 */ function throttle<R>(fn: Function, delay = 0) { let lastTime = 0 return function (...args: unknown[]) { const now = Date.now() if (now - lastTime >= delay) { let data = fn(...args) lastTime = now return data as R } } } /** * @name debounce 防抖 */ function debounce<R>(fn: Function, delay = 0) { let timerId: number let immediately = true return function (...args: unknown[]) { clearTimeout(timerId) return new Promise<R>((resolve) => { if (immediately) { immediately = false resolve(fn(...args)) } else { timerId = setTimeout(() => { immediately = true resolve(fn(...args)) }, delay) } }) } }
👩💻 通过 throttleWait 、debounceWait参数匹配,将throttle、debounce 方法运用在 useRequst 返回的run方法中,实现防抖、节流。
function useRequest<T extends TypeUseRequestAsyncFn, R = Awaited<ReturnType<T>>>( fn: T, params?: TypeUseRequestParameters ) { // ... return { run: params?.throttleWait ? throttle<R>(run, params.throttleWait) // 节流 : params?.debounceWait ? debounce<R>(run, params.debounceWait) // 防抖 : run, // 请求方法 data, loading, }; };
依赖刷新
👀 Vue3的 watch API(官方文档),可通过监听ref、reactive对象变化,从而调用回调函数,实现依赖刷新功能。useRequest可以借助此特性,实现该场景功能需求。
import { watch } from 'vue'; function useRequest<T extends TypeUseRequestAsyncFn, R = Awaited<ReturnType<T>>>( fn: T, params?: TypeUseRequestParameters ) { // ... const returns = { run: params?.throttleWait ? throttle<R>(run, params.throttleWait) // 节流 : params?.debounceWait ? debounce<R>(run, params.debounceWait) // 防抖 : run, // 请求方法 data, loading, }; // 实现 “依赖刷新” watch(params?.refreshDeps || Function, () => returns.run(), { deep: true }); return returns; };
这样,一个最基本的 useRequest Hooks就实现了,有其他的需求可以继续添加。