UNPKG

ym-userequest

Version:
771 lines (639 loc) 18.8 kB
![NPM Version](https://img.shields.io/npm/v/ym-userequest) [![codecov](https://codecov.io/gh/YeMao-Zi/ym-useRequest/graph/badge.svg?token=PELX6B0U8F)](https://codecov.io/gh/YeMao-Zi/ym-useRequest) [English](./README-en_US.md) | 简体中文 ## 介绍 接口的手动管理工具 ## 安装 npm install ym-userequest ## 使用 <!-- TOC --> - [手动执行](#手动执行) - [自动执行一次并带默认参数](#自动执行一次并带默认参数) - [取消响应](#取消响应) - [监听响应式数据并自动执行更新数据](#监听响应式数据并自动执行更新数据) - [延时 loading](#延时-loading) - [修改 data 数据](#修改-data-数据) - [defaultData](#defaultdata) - [wrappedPromise](#wrappedpromise) - [mutate](#mutate) - [轮询](#轮询) - [轮询错误重试](#轮询错误重试) - [轮询次数](#轮询次数) - [允许请求](#允许请求) - [函数防抖](#函数防抖) - [函数节流](#函数节流) - [缓存](#缓存) - [错误重试](#错误重试) - [requestTick](#requesttick) - [监听浏览器页面切换](#监听浏览器页面切换) - [获取指定的 useRequest 实例](#获取指定的-userequest-实例) - [自定义插件](#自定义插件) - [内置插件 useReadyPlugin](#内置插件-usereadyplugin) - [自定义 Fetch 请求取消插件](#自定义-fetch-请求取消插件) - [中间件](#中间件) - [全局配置](#全局配置) - [所有配置项](#所有配置项) - [所有返回项](#所有返回项) <!-- /TOC --> ### 手动执行 ```ts import { useRequest } from 'ym-userequest'; const { data, params, loading, run, runAsync, refresh, refreshAsync, cancel } = useRequest(somePromise, { manual: true, }); const somePromise = () => { return new Promise((resolve, reject) => { resolve('success'); }); }; run(); // 手动触发一次 // runAsync(); // 同 run , 但返回的是一个 promise // refresh(); // 使用上次的参数重新请求 // refreshAsync(); // 同 refresh , 但返回的是一个 promise // cancel(); // 取消请求 ``` ### 自动执行一次并带默认参数 ```ts // 多参数请求 const somePromise1 = (params1, params2) => { return new Promise((resolve, reject) => { resolve({ params1, params2 }); }); }; const { data, loading } = useRequest(somePromise1, { defaultParams: ['params1', 'params2'], }); // 单参数请求 const somePromise2 = (params1) => { return new Promise((resolve, reject) => { resolve({ params1 }); }); }; const { data, loading } = useRequest(somePromise2, { defaultParams: 'params1', // or ['params1'] }); ``` > defaultParams 传入为数组时表示函数的多个参数,所以如果想要传入单个参数且参数本身为数组,应该使用 [array] 这样传参 ### 取消响应 ```ts const request = () => fetch('https://httpbin.org/delay/3', { method: 'POST', body: JSON.stringify(params), signal, }); const { data, cancel } = useRequestI(request); cancel(); ``` > 调用 cancel 时只是忽略掉当前的请求,不会中断正在请求的请求。想要真正中断请求可以自行实现,参考下面的自定义 Fetch 请求取消插件 ### 监听响应式数据并自动执行更新数据 ```ts const pages = reactive({ size: 1, }); const somePromise = (number: number): Promise<any[]> => { return new Promise((resolve) => { setTimeout(() => { resolve(new Array(number).fill(1)); }, 1000); }); }; const refreshDepsParams = computed(() => pages.size); const { data, refresh, loading } = useRequest(somePromise, { defaultParams: pages.size, refreshDeps: [() => pages.size], // 监听的依赖 // 可选,依赖变更后执行的参数,不传则在依赖变更后执行 refresh, // 如果为函数会执行该函数并将返回值作为参数 refreshDepsParams: refreshDepsParams, // 不想监听后立即请求,可以传递一个没有返回值的函数,该函数会被调用 // refreshDepsParams: () => {}, }); const onClick = () => { pages.size++; }; ``` ### 延时 loading 使用 loadingDelay 定义一个 loading 的延时时间,避免请求时间较短时出现 loading 闪烁 ```ts const somePromise = (params) => { return new Promise((resolve, reject) => { setTimeout(() => { resolve(params); }, 300); }); }; const { data, loading } = useRequest(somePromise, { defaultParams: ['params1'], loadingDelay: 1000, // 1s 内loading状态都不会改变 }); ``` ### 修改 data 数据 #### defaultData 因为 data 为 ref ,无法进行解构赋默认值,可通过 defaultData 设置默认值 ```ts const somePromise = () => { return new Promise((resolve, reject) => { resolve(1); }); }; const { data, loading, mutate } = useRequest(somePromise, { manual: true, defaultData: 3, // return data:Ref<3> // defaultData: shallowRef<3>, // return data:ShallowRef<3> }); ``` #### wrappedPromise 通过包装请求的方式来改变返回值,同时返回值的类型也对应改变 ```ts import { wrappedPromise } from 'ym-userequest'; const somePromise = () => { return new Promise((resolve, reject) => { resolve(1); }); }; const { data, loading, mutate, requestTick } = useRequest( wrappedPromise(somePromise, (result, params) => { return { value: result }; }), ); requestTick(() => { console.log(data.value); // {value:1} }); ``` #### mutate ```ts const somePromise = () => { return new Promise((resolve, reject) => { resolve(1); }); }; const { data, loading, mutate } = useRequest(somePromise, { manual: true }); mutate(5555); console.log(data.value); // 5555 const onChange = () => { mutate((res) => { return res + 1; }); }; onChange(); console.log(data.value); // 2 ``` ### 轮询 ```ts const somePromise = () => { return new Promise((resolve, reject) => { resolve(1); }); }; // 每 3000 ms 进行一次请求 const { data, run, cancel } = useRequest(somePromise, { pollingInterval: 3000, }); const onRun = () => { run(); // 继续轮询 }; const onCancel = () => { cancel(); // 停止轮询 }; ``` #### 轮询错误重试 ```ts const errPromise = () => { return new Promise((resolve, reject) => { reject('error'); }); }; const { data, run, cancel } = useRequest(errPromise, { pollingInterval: 3000, pollingErrorRetryCount: 3, // 请求错误重试,将在三次轮询后不再轮询 }); ``` #### 轮询次数 在进行一次 cancel 后,轮询次数会被清空 ```ts const somePromise = () => { return new Promise((resolve, reject) => { resolve(1); }); }; const { cancel, pollingCount } = useRequest(somePromise, { pollingInterval: 100, onFinally() { if (pollingCount.value === 3) { cancel(); } }, }); ``` ### 允许请求 ready 参数控制本次请求是否被允许,若 ready 为 false ,该请求始终不会被允许 ```ts const somePromise = () => { return new Promise((resolve, reject) => { resolve(1); }); }; const ready = ref(false); const { data, run } = useRequest(somePromise, { ready, // or ready:()=> ready.value, }); console.log(data.value); // null ready.value = true; run(); console.log(data.value); // 1 ``` ### 函数防抖 ```ts const somePromise = () => { return new Promise((resolve, reject) => { resolve(1); }); }; const { data, run } = useRequest(somePromise, { manual: true, debounceWait: 2000, debounceOptions: { // 参数同 loadsh 的 debounce leading: true, trailing: false, }, }); const onRun = () => { run(); }; ``` ### 函数节流 ```ts const somePromise = () => { return new Promise((resolve, reject) => { resolve(1); }); }; const { data, run } = useRequest(somePromise, { manual: true, throttleWait: 2000, throttleOptions: { // 参数同 loadsh 的 debounce leading: true, trailing: false, }, }); const onRun = () => { run(); }; ``` ### 缓存 > 设置了 cacheKey,组件在第二次加载时,会优先返回缓存的内容,然后在背后重新发起请求 ```ts import { useRequest, clearCache } from 'ym-userequest'; const somePromise = (value) => { return new Promise((resolve, reject) => { resolve(value); }); }; const { data, run } = useRequest(somePromise, { manual: true, cacheKey: 'test', cacheTime: 5000, staleTime: -1, // 自定义设置缓存 // setCache(cacheKey, data) { // localStorage.setItem(cacheKey, JSON.stringify(data)); // }, // 自定义获取缓存 // getCache(cacheKey) { // return JSON.parse(localStorage.getItem(cacheKey) || 'null'); // }, }); run(1); // 1 run(2); // 1 // 手动清除缓存 clearCache(params?:string[]|string) // clearCache() setTimeOut(() => { run(2); console.log(data.value); // 2 }, 5000); ``` ### 错误重试 当请求失败后重新请求次数 ```ts const errorPromise = () => { return new Promise((resolve, reject) => { setTimeout(() => { reject('error'); }, 1000); }); }; let count = 0; const { data } = useRequest(errorPromise, { retryCount: 3, // retryInterval:1000, onError() { count += 1; console.log(count); }, }); // count:1 // count:2 // count:3 // count:4 ``` ### requestTick 等待请求完成后的操作回调 ```ts const getData = (value = 1, time = 1000): Promise<number> => { return new Promise((resolve) => { setTimeout(() => { resolve(value); }, time); }); }; const { data, run: run1, requestTick } = useRequest(() => getData(3), { manual: true }); const { run: run2 } = useRequest(getData, { manual: true }); const runAll = async () => { run1(); run2(); console.log(data.value); // undefined await requestTick(() => { console.log(data.value); // 3 }); console.log(data.value); // 3 }; runAll(); ``` ### 监听浏览器页面切换 ```ts useRequest(getData, { // 在浏览器页面重新显示时,重新发起请求 refreshOnWindowFocus: true, // 间隔1000ms内不重新请求 focusTimespan: 1000, // 离开浏览器页面时,取消请求 cancelOnWindowBlur: true, }); ``` ### 获取指定的 useRequest 实例 可以运用在不同的组件,获取指定的 useRequest 实例 ```ts import { useRequest, getRequest } from 'ym-request'; const instance1 = useRequest(getData, { id: 'getData', manual: true, }); instance1.run(2); const instance2 = getRequest('getData'); console.log(instance1.data, instance2.data); // 2,2 instance2.run(3); console.log(instance1.data, instance2.data); // 3,3 ``` ### 自定义插件 在 instance 中可以获取所有返回项 在 options 中可以获取所有配置项 插件返回对象中,onBefore 中返回 returnNow 为 true 时,将不再发起请求,返回 returnData 将作为 data 数据返回 onInit 可以用来改写请求 onSuccess 等其他函数对应请求的各个时机 #### 内置插件 useReadyPlugin ```ts import { definePlugins, TypeChecker, Plugin } from 'ym-userequest'; import { Ref, unref } from 'vue'; const useReadyPlugin: Plugin<any, any[]> = (instance, options) => { const { ready = true } = options; return { onBefore() { const _ready = TypeChecker.isFunction(ready) ? (ready as () => boolean | Ref<boolean>)() : ready; if (!unref(_ready)) { instance.loading.value = false; return { returnNow: true, // returnData: instance.data.value, }; } }, // 所有的返回签名,对应 useRequest 的回调 // onBefore: (params: P) => onBeforePlugin | void; // onInit: (service: (...args: P) => Promise<R>) => { servicePromise: Promise<R> }, // onSuccess(data: R, params: P): void, // onError(error: Error, params: P): void, // onFinally(params: P, data: R, error: Error): void, // onCancel(): void, // onMutate(data: R): void, }; }; // 注册 definePlugins([useReadyPlugin]); ``` #### 自定义 Fetch 请求取消插件 插件 ```ts // useFetchCancelPlugin.ts import { type Plugin } from 'ym-userequest'; export const useFetchCancelPlugin: Plugin<any, any[]> = (instance, { controller }) => { if (!controller) { return {}; } let currentController = new AbortController(); return { onBefore() { if (currentController.signal.aborted) { currentController = new AbortController(); } }, onInit(service) { return { servicePromise: service(...instance.params.value, currentController.signal), }; }, onCancel() { currentController.abort(); }, }; }; ``` 注册 ```ts // main.ts import { createApp } from 'vue'; import App from './App.vue'; import { useFetchCancelPlugin } from './useFetchCancelPlugin'; import { definePlugins } from 'ym-userequest'; definePlugins([useFetchCancelPlugin]); const app = createApp(App); app.mount('#app'); ``` 使用 ```ts import { useRequest } from 'ym-userequest'; const request = (params: any, signal?: AbortSignal) => fetch('https://httpbin.org/delay/3', { method: 'POST', body: JSON.stringify(params), signal, }); const { data, loading, status, cancel, run } = useRequest(request, { manual: true, controller: true, onCancel() { console.log('onCancel'); }, }); run(123); setTimeout(() => { cancel(); }, 1000); ``` ### 中间件 ```ts const logs: string[] = []; const logger1 = (useRequestNext: any) => { return (service: any, options: any, plugins: any) => { logs.push('logger1 enter'); const extendedService = (...args: any[]) => { logs.push('logger1 service'); return service(...args); }; const next = useRequestNext(extendedService, options, plugins); logs.push('logger1 exit'); return next; }; }; const logger2 = (useRequestNext: any) => { return (service: any, options: any, plugins: any) => { logs.push('logger2 enter'); const extendedService = (...args: any[]) => { logs.push('logger2 service'); return service(...args); }; const next = useRequestNext(extendedService, options, plugins); logs.push('logger2 exit'); return next; }; }; useRequest(getData, { manual: true, use: [logger1, logger2], }); run(); // logs: ['logger1 enter', 'logger2 enter', 'logger2 exit', 'logger1 exit', 'logger2 service', 'logger1 service']; ``` ## 全局配置 在组件中使用时该组件及其子组件的 useRequest 的 options 默认为 useRequestConfig 传入的 options ```ts useRequestConfig({ manual: true }); ``` ## 所有配置项 ```ts { // 是否手动发起请求,默认 false ,会在初始化后自动执行一次 run manual?: boolean; // 设置默认 data,也可用于指定 data 为 ShallowRef 或 Ref // 如果传入值为非响应式,将被转化为 ref defaultData?: R | Ref<R>; // 当 manual 为 false 时,自动执行的默认参数 defaultParams?: Params<P>; // 监听依赖 refreshDeps?: WatchSource<any>[] | WatchSource<any>; // 依赖变更后的执行参数,若为函数会执行该函数,有返回值则会将返回值作为参数发起一次请求 refreshDepsParams?: Params<P> | ((value: P, oldValue: P) => void | Params<P>); // 请求延时 loadingDelay?: number; // 轮询 pollingInterval?: Ref<number> | number; // 轮询错误重试 pollingErrorRetryCount?: number; // 错误重试次数 retryCount?:number; // 重试时间间隔 // 如果不设置,默认采用简易的指数退避算法,取 1000 * 2 ** retryCount, // 也就是第一次重试等待 2s,第二次重试等待 4s,以此类推,如果大于 30s,则取 30s retryInterval?:number; // 是否允许请求(变更时不会触发自动请求) ready?: (() => Ref<boolean> | boolean) | (Ref<boolean> | boolean); // 防抖等待时间 debounceWait?: number; // 防抖函数属性 debounceOptions?: { // 是否在延迟开始前执行 leading?: boolean; // 是否在延迟开始后执行 trailing?: boolean; // 允许被延迟的最大值 maxWait?: number; }; // 节流等待时间 throttleWait?: number; // 节流函数属性 throttleOptions?: { // 是否在延迟开始前执行 leading?: boolean; // 是否在延迟开始后执行 trailing?: boolean; }; // 缓存的唯一标识 cacheKey?: string | ((params?: P) => string); // 缓存时间,默认: 5 * 60 * 1000 // 超出缓存时间会清除对应缓存 // 另外需要注意的是,当缓存失效时,无论 staleTime 是否存在都会重新请求 cacheTime?: number; // 缓存数据保持新鲜时间(什么时候会重新发送请求更新缓存),默认 0,若为-1表示始终不再发送请求 // 即在新鲜时间内都直接获取缓存中的数据 staleTime?: number; // 自定义获取缓存 getCache?: (cacheKey: string) => CacheData; // 自定义设置缓存 setCache?: (cacheKey: string, cacheData: CacheData) => void; // 在浏览器页面重新显示时,是否重新发起请求 refreshOnWindowFocus?: Ref<boolean> | boolean; // 重新请求间隔,单位为毫秒,默认5000 focusTimespan?: Ref<number> | number; // 离开浏览器页面时,是否取消请求 cancelOnWindowBlur?: Ref<boolean> | boolean; // 获取缓存时回调 onCache?: (response: R) => void; // 请求前回调 onBefore?: (params: P) => void; // 每次请求被执行时回调,可以在该回调中获取每次请求的响应 onRequest?: ({ params, response, error, abort }: { params: P; response: R; error: any; abort: boolean }) => void; // 成功回调(在该回调之后 data 被重新赋值) onSuccess?: (response: R, params: P) => MaybePromise<void | R>; // 失败回调 onError?: (err: any, params: P) => void; // 接口完成回调 onFinally?: () => void; // 忽略当前 Promise 时执行回调 onCancel?:()=>void; // 中间件 use?: UseRequestMiddleware<R, P>[]; } ``` ## 所有返回项 ```ts { // 返回请求执行成功后的返回的数据 data: Ref<R>; // 返回请求的执行状态 loading: Ref<boolean>; // 返回请求失败的错误信息 error?: Ref<any>; // 返回本次请求的参数 params?: Ref<P>; // 进行轮询时累计轮询次数 pollingCount:Ref<number> // 手动执行请求(返回promise) runAsync: (...arg: P) => Promise<R>; // 手动执行请求 run: (...arg: P) => void; // 忽略当前 Promise 的响应 cancel: () => void; // 手动刷新请求 refresh: () => void; // 手动刷新请求(返回promise) refreshAsync: () => Promise<R>; // 修改返回的data数据 mutate: (newData: R) => void | (arg: (oldData: R) => R) => void; // data 处理进程,默认undfined,表示未被请求过 status: Ref<'pending' | 'settled'>; // 等待接口完成 requestTick: (callback?: (context: { params: P; data: R }) => void) => Promise<{ params: P; data: R }>; } ```