欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Vue3?TypeScript?實現(xiàn)useRequest詳情

 更新時間:2022年05月26日 15:57:38   作者:??woodenF????  
本文介紹了Vue3?TypeScript實現(xiàn)useRequest詳情,useRequest可能是目前社區(qū)中最強大,最接地氣的請求類?Hooks了??梢愿采w99%的網(wǎng)絡(luò)請求場景,無論是讀還是寫,無論是普通請求還是分頁請求,無論是緩存還是防抖節(jié)流,通通都能支持,關(guān)于其介紹需要的小伙伴可以參考一下

前言:

自從 Vue3 更新之后,算是投入了比較大的精力寫了一個較為完善的Vue3.2 + Vite2 + Pinia + Naive UI的B端模版,在做到網(wǎng)絡(luò)請求這一塊的時候,最初使用的是VueRequestuseRequest,但是因為VueRequestuseRequestcancel關(guān)閉請求并不是真正的關(guān)閉,對我個人來說,還是比較介意,于是在參考aHooksVueRequest的源碼之后,差不多弄了一個簡易的useRequest,使用體驗還算ok,但是因為個人能力以及公司業(yè)務(wù)的問題,我的版本只支持axios,不支持fetch,算是作為公司私有的庫使用,沒有考慮功能的大而全,也只按VueRequest的官網(wǎng),實現(xiàn)了一部分我認為最重要的功能。

寫的比較混亂,中間是一部分思考,可以直接拖到最后看實現(xiàn),再回來看一下我為什么選擇這么做,歡迎討論。

效果展示

一個基礎(chǔ)的useRequest示例,支持發(fā)起請求 取消請求 請求成功信息 成功回調(diào) 錯誤捕獲

queryKey示例,單個useRequest管理多個相同請求。

其余還是依賴更新 重復(fù)請求關(guān)閉 防抖 節(jié)流等功能

Axios

既然咱們使用TypeScriptaxios,為了使axios能滿足咱們的使用需求以及配合TypeScript的編寫時使用體驗,咱們對axios進行一個簡單的封裝。

interface

// /src/hooks/useRequest/types.ts
import { AxiosResponse, Canceler } from 'axios';
import { Ref } from 'vue';
// 后臺返回的數(shù)據(jù)類型
export interface Response<T> {
    code: number;
    data: T;
    msg: string;
}
// 為了使用方便,對 AxiosResponse 默認添加我們公用的 Response 類型
export type AppAxiosResponse<T = any> = AxiosResponse<Response<T>>;

// 為了 useRequest 使用封裝的類型
export interface RequestResponse<T> {
    instance: Promise<AppAxiosResponse<T>>;
    cancel: Ref<Canceler | undefined>;
}

axios的簡單封裝

因為咱們現(xiàn)在沒有接入業(yè)務(wù),所以axios只需要簡單的封裝能支持咱們useRequest的需求即可。

import { ref } from 'vue';
import { AppAxiosResponse, RequestResponse } from './types';
import axios, { AxiosRequestConfig, Canceler } from 'axios';
const instance = axios.create({
  timeout: 30 * 1000,
  baseURL: '/api'
});
export function request<T>(config: AxiosRequestConfig): RequestResponse<T> {
  const cancel = ref<Canceler>();
  return {
    instance: instance({
      ...config,
      cancelToken: new axios.CancelToken((c) => {
        cancel.value = c;
      })
    }),
    cancel
  };
}

例:

import { IUser } from '@/interface/User';
export function getUserInfo(id: number) {
  return request<IUser>({
    url: '/getUserInfo',
    method: 'get',
    params: {
      id
    }
  });
}

需要注意的是,示例中的錯誤信息經(jīng)過了統(tǒng)一性的封裝,如果希望錯誤有一致性的表現(xiàn),可以封裝一個類型接收錯誤,建議與后臺返回的數(shù)據(jù)結(jié)構(gòu)一致。

現(xiàn)在,咱們使用這個request函數(shù),傳入對應(yīng)的泛型,就可以享受到對應(yīng)的類型提示。

useRequest

如何使用

想要設(shè)計useRequest,那現(xiàn)在思考一下,什么樣的useRequest使用起來,能讓我們感到快樂,拿上面的基礎(chǔ)示例queryKey示例來看,大家可以參考一下VueRequest或者aHooks的用法,我是看了他們的用法來構(gòu)思我的設(shè)計的。

比如一個普通的請求,我希望簡單的使用data、loadingerr等來接受數(shù)據(jù),比如:

const { run, data, loading, cancel, err } = useRequest(getUserInfo, {
    manual: true
})

那 useRequest 的簡單模型好像是這樣的

export function useRequest(service, options) {
    return {
        data,
        run,
        loading,
        cancel,
        err
    }
}

傳入一個請求函數(shù)配置信息,請求交由useRequest內(nèi)部接管,最后將data loading等信息返回即可。

那加上queryKey

const { run, querise } = useRequest(getUserInfo, {
    manual: true,
    queryKey: (id) => String(id) 
})

似乎還要返回一個querise,于是變成了

export function useRequest(service, options) {
    return {
        data,
        run,
        loading,
        cancel,
        err,
        querise
    }
}

對應(yīng)的querise[key]選項,還要額外維護data loading等屬性,這樣對于useRequest內(nèi)部來說是不是太割裂了呢,大家可以嘗試一下,因為我就是一開始做簡單版本之后再來考慮queryKey功能的,代碼是十分難看的。

添加泛型支持

上面的偽代碼我們都沒有添加泛型支持,那我們需要添加哪些泛型,上面request的例子其實比較明顯了

import { IUser } from '@/interface/User';
export function getUserInfo(id: number) {
  return request<IUser>({
    url: '/getUserInfo',
    method: 'get',
    params: {
      id
    }
  });
}

對于id,作為請求參數(shù),我們每一個請求都不確定,這里肯定是需要一個泛型的,IUser作為返回類型的泛型,需要被useRequest正確識別,必然也是需要一個泛型的。

其中,請求參數(shù)的泛型,為了使用的方便,我們定義其extends any[],必須是一個數(shù)組,使用...args的形式傳入到requestinstance中執(zhí)行。

service的類型需要與request類型保持一致, options的類型按需要實現(xiàn)的功能參數(shù)添加,于是,我們得到了如下一個useRequest

// /src/hooks/useRequest/types.ts
export type Service<T, P extends any[]> = (...args: P) => RequestResponse<T>;
// 可按對應(yīng)的配置項需求擴展
export interface Options<T, P extnds any> {
  // 是否手動發(fā)起請求
  manual?: boolean;
  // 當(dāng) manual 為false時,自動執(zhí)行的默認參數(shù)
  defaultParams?: P;
  // 依賴項更新
  refreshDeps?: WatchSource<any>[];
  refreshDepsParams?: ComputedRef<P>;
  // 是否關(guān)閉重復(fù)請求,當(dāng)queryKey存在時,該字段無效
  repeatCancel?: boolean;
  // 并發(fā)請求
  queryKey?: (...args: P) => string;
  // 成功回調(diào)
  onSuccess?: (response: AxiosResponse<Response<T>>, params: P) => void;
  // 失敗回調(diào)
  onError?: (err: ErrorData, params: P) => void;
}
// /src/hooks/useRequest/index.ts
export function useRequest<T, P extends any[]>(
    service: Service<T, P>,
    options: Options<T, P> = {}
){
    return {
        data, // data 類型為T
        run,
        loading,
        cancel,
        err,
        querise
    }
}

queryKey的問題

上面我們提到了,queryKey請求普通請求如果單獨維護,不僅割裂,而且代碼還很混亂,那有沒有什么辦法來解決這個問題呢,用js的思想來看這個問題,假設(shè)我現(xiàn)在有一個對象querise,我需要將不同請求參數(shù)的請求相關(guān)數(shù)據(jù)維護到querise中,比如run(1),那么querise應(yīng)該為

const querise = {
  1: {
      data: null,
      loading: false
      ...
  }
}

這是在queryKey的情況下,那沒有queryKey呢?很簡單,維護到default對象唄,即

const querise = {
  default: {
      data: null,
      loading: false
      ...
  }
}

為了確保默認key值的唯一性,我們引入Symbol,即

const defaultQuerise = Symbol('default');
const querise = {
  [defaultQuerise]: {
      data: null,
      loading: false
      ...
  }
}

因為我們會使用reactive包裹querise,所以想要滿足非queryKey請求時,使用默認導(dǎo)出的data loading err等數(shù)據(jù),只需要

return {
    run,
    querise,
    ...toRefs(querise[defaulrQuerise])
}

好了,需要討論的問題完了,我們來寫代碼

完整代碼

// /src/hooks/useRequest/types.ts
import { Canceler, AxiosResponse } from 'axios';
import { ComputedRef, WatchSource, Ref } from 'vue';
export interface Response<T> {
  code: number;
  data: T;
  msg: string;
}
export type AppAxiosResponse<T = any> = AxiosResponse<Response<T>>;
export interface RequestResponse<T>{
  instance: Promise<AppAxiosResponse<T>>;
  cancel: Ref<Canceler | undefined>
}
export type Service<T, P extends any[]> = (...args: P) => RequestResponse<T>;
export interface Options<T, P extends any[]> {
  // 是否手動發(fā)起請求
  manual?: boolean;
  // 當(dāng) manual 為false時,自動執(zhí)行的默認參數(shù)
  defaultParams?: P;
  // 依賴項更新
  refreshDeps?: WatchSource<any>[];
  refreshDepsParams?: ComputedRef<P>;
  // 是否關(guān)閉重復(fù)請求,當(dāng)queryKey存在時,該字段無效
  repeatCancel?: boolean;
  // 重試次數(shù)
  retryCount?: number;
  // 重試間隔時間
  retryInterval?: number;
  // 并發(fā)請求
  queryKey?: (...args: P) => string;
  // 成功回調(diào)
  onSuccess?: (response: AxiosResponse<Response<T>>, params: P) => void;

  // 失敗回調(diào)
  onError?: (err: ErrorData, params: P) => void;
}
export interface IRequestResult<T> {
  data: T | null;
  loading: boolean;
  cancel: Canceler;
  err?: ErrorData;
}
export interface ErrorData<T = any> {
  code: number | string;
  data: T;
  msg: string;
}
// /src/hooks/useRequest/axios.ts
import { ref } from 'vue';
import { AppAxiosResponse, RequestResponse } from './types';
import axios, { AxiosRequestConfig, Canceler } from 'axios';
const instance = axios.create({
  timeout: 30 * 1000,
  baseURL: '/api'
});

instance.interceptors.request.use(undefined, (err) => {
  console.log('request-error', err);
});

instance.interceptors.response.use((res: AppAxiosResponse) => {
  if(res.data.code !== 200) {
    return Promise.reject(res.data);
  }
  return res;
}, (err) => {
  if(axios.isCancel(err)) {
    return Promise.reject({
      code: 10000,
      msg: 'Cancel',
      data: null
    });
  }
  if(err.code === 'ECONNABORTED') {
    return Promise.reject({
      code: 10001,
      msg: '超時',
      data: null
    });
  }
  console.log('response-error', err.toJSON());
  return Promise.reject(err);
});
export function request<T>(config: AxiosRequestConfig): RequestResponse<T> {
  const cancel = ref<Canceler>();
  return {
    instance: instance({
      ...config,
      cancelToken: new axios.CancelToken((c) => {
        cancel.value = c;
      })
    }),
    cancel
  };
}
import { isFunction } from 'lodash';
import { reactive, toRefs, watch } from 'vue';
import { IRequestResult, Options, Service, ErrorData } from './types';
const defaultQuerise = Symbol('default');
export function useRequest<T, P extends any[]>(
  service: Service<T, P>,
  options: Options<T, P> = {}
) {
  const {
    manual = false,
    defaultParams = [] as unknown as P,
    repeatCancel = false,
    refreshDeps = null,
    refreshDepsParams = null,
    queryKey = null
  } = options;

  const querise = reactive<Record<string | symbol, IRequestResult<T>>>({
    [defaultQuerise]: {
      data: null,
      loading: false,
      cancel: () => null,
      err: undefined
    }
  });
  const serviceFn = async (...args: P) => {
    const key = queryKey ? queryKey(...args) : defaultQuerise;
    if (!querise[key]) {
      querise[key] = {} as any;
    }
    if (!queryKey && repeatCancel) {
      querise[key].cancel();
    }
    querise[key].loading = true;
    const { instance, cancel } = service(...args);
    querise[key].cancel = cancel as any;
    instance
      .then((res) => {
        querise[key].data = res.data.data;
        querise[key].err = undefined;
        if (isFunction(options.onSuccess)) {
          options.onSuccess(res, args);
        }
      })
      .catch((err: ErrorData) => {
        querise[key].err = err;
        if (isFunction(options.onError)) {
          options.onError(err, args);
        }
      })
      .finally(() => {
        querise[key].loading = false;
      });
  };

  const run = serviceFn;
  // 依賴更新
  if (refreshDeps) {
    watch(
      refreshDeps,
      () => {
        run(...(refreshDepsParams?.value || ([] as unknown as P)));
      },
      { deep: true }
    );
  }

  if (!manual) {
    run(...defaultParams);
  }

  return {
    run,
    querise,
    ...toRefs(querise[defaultQuerise])
  };
}

需要防抖 節(jié)流 錯誤重試等功能,僅需要擴展Options類型,在useRequest中添加對應(yīng)的邏輯即可,比如使用lodash包裹run函數(shù),這里只是將最基本的功能實現(xiàn)搞定了,一部分小問題以及擴展性的東西沒有過分糾結(jié)。

結(jié)語

到此這篇關(guān)于Vue3 TypeScript 實現(xiàn)useRequest詳情的文章就介紹到這了,更多相關(guān)TypeScript實現(xiàn) useRequest內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • vue實現(xiàn)垂直無限滑動日歷組件

    vue實現(xiàn)垂直無限滑動日歷組件

    這篇文章主要為大家詳細介紹了vue實現(xiàn)垂直無限滑動日歷組件,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-04-04
  • Vue整合Node.js直連Mysql數(shù)據(jù)庫進行CURD操作過程詳解

    Vue整合Node.js直連Mysql數(shù)據(jù)庫進行CURD操作過程詳解

    這篇文章主要給大家分享Vue整合Node.js,直連Mysql數(shù)據(jù)庫進行CURD操作的詳細過程,文中有詳細的代碼講解,具有一定的參考價值,需要的朋友可以參考下
    2023-07-07
  • vue3整合springboot打完整jar包

    vue3整合springboot打完整jar包

    本文主要介紹了vue3整合springboot打完整jar包,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-09-09
  • vue渲染時閃爍{{}}的問題及解決方法

    vue渲染時閃爍{{}}的問題及解決方法

    v-if和v-show可能是日常開發(fā)中最常用的兩個指令,雖然看上去兩者功能是類似的,但是兩者還是存在很大區(qū)別的。接下來通過本文給大家分享vue渲染時閃爍{{}}的問題及解決方法,感興趣的朋友一起看看吧
    2018-03-03
  • vue3項目vite.config.js配置代理、端口、打包名以及圖片壓縮

    vue3項目vite.config.js配置代理、端口、打包名以及圖片壓縮

    這篇文章主要給大家介紹了關(guān)于vue3項目vite.config.js配置代理、端口、打包名以及圖片壓縮的相關(guān)資料,因為3.0版本中vue已經(jīng)內(nèi)置了很多關(guān)于webpack的配置,一般情況下開箱即用,需要修改則可以在vue.config.js文件中完成,需要的朋友可以參考下
    2023-12-12
  • Vue進階之利用transition標(biāo)簽實現(xiàn)頁面跳轉(zhuǎn)動畫

    Vue進階之利用transition標(biāo)簽實現(xiàn)頁面跳轉(zhuǎn)動畫

    這篇文章主要為大家詳細介紹了Vue如何利用transition標(biāo)簽實現(xiàn)頁面跳轉(zhuǎn)動畫,文中的示例代碼講解詳細,感興趣的小伙伴可以跟隨小編一起一下
    2023-08-08
  • vue項目引入百度地圖BMapGL鼠標(biāo)繪制和BMap輔助工具

    vue項目引入百度地圖BMapGL鼠標(biāo)繪制和BMap輔助工具

    這篇文章主要為大家介紹了vue項目引入百度地圖BMapGL鼠標(biāo)繪制和BMap輔助工具的踩坑分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-02-02
  • vue3.0運行npm run dev報錯Cannot find module node:url

    vue3.0運行npm run dev報錯Cannot find module&

    本文主要介紹了vue3.0運行npm run dev報錯Cannot find module node:url,因為使用的node版本是14.15.1低于15.0.0導(dǎo)致,具有一定的參考價值,感興趣的可以了解一下
    2023-10-10
  • Vue出現(xiàn)did you register the component correctly?解決方案

    Vue出現(xiàn)did you register the component 

    這篇文章主要介紹了Vue出現(xiàn)did you register the component correctly?解決方案,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-03-03
  • vue.js刪除動態(tài)綁定的radio的指定項

    vue.js刪除動態(tài)綁定的radio的指定項

    這篇文章主要介紹了vue.js刪除動態(tài)綁定的radio的指定項,需要的朋友可以參考下
    2017-06-06

最新評論