設計vue3的請求實體工廠

Mr.蘇發表於2021-03-27

設計一個vue3的請求實體工廠

描述

PS: 這裡的方法是基於Vue的class寫法的,對於setup寫法不適用。

主要功能是建立一個具備一個請求完整封裝的例項,可以便捷的請求,取消請求,獲取資料和請求狀態等功能

下面是通過typescript和Vue實現,結合typescript的宣告,這個generateRequest方法才會得到昇華

實現

實現分下面3個部分

1.構建一個基礎請求方法

2.建立具體請求的方法

3.generateRequest對請求的封裝

構建一個基礎請求方法

這裡沒啥說的,直接上axios做一個簡單的封裝

request.ts

import axios from 'axios'
const option = {...}
export request = axios.create(option)

建立具體請求的方法

下面是對請求的宣告檔案

xxx.d.ts

// 對請求返回的請求體宣告
export class Result<T0 = any> {
    /** code */
    code?:number;

    /** data */
    data?:T0;

    /** error_code */
    error_code?:string;

    /** error_message */
    error_message?:string;

    /** success */
    success?:boolean;
}
// 文章介面返回data宣告
export interface ArticleData {
    id:number;
    title:string;
    cover_url:string;
    content:string;
    read_count:string;
    abstract:string;
    time:number;
}

下面是請求的定義

api.ts

import { request } from './request'
import { AxiosRequestConfig } from 'axios'
import { Result, ArticleData } from './xxx'
/** 獲取文章 */
export const getArticle = (id:number, config?:AxiosRequestConfig) => request.get<Result<ArticleData>>(`/api/article?id=${id}`)

generateRequest對請求的封裝

這裡主要用到的知識點有:

1.由對映型別進行推斷(對引數進行拆包)

2.對上下文的理解

3.vue資料響應式機制

下面是一個基礎實現,還可以繼續封裝對業務場景有用的方法或屬性

功能

  • 具備響應式屬性:data(返回的資料)loading、isError、params(請求的引數)
  • 可以傳入資料處理的方法format
  • 設定data初始值initData
  • 取消請求的cancel方法
  • 發起請求的run方法

utils.ts

import axios, { AxiosPromise, AxiosRequestConfig } from 'axios';
interface GenerateRequestHook<T, K> {
  data:T | undefined;
  run:(params?:K) => AxiosPromise<T>;
  loading:boolean;
  isError:boolean;
  uid:number;
  cancel:((msg?:string) => void);
  params:K | undefined;
}
interface Config<L> {
  config?:AxiosRequestConfig;
  format?:(data:any) => any;
  initData?:L;
}
let _uid = 0;
/**
 * @description 返回請求的封裝實體的request,僅可以在class寫法的組建內使用
 * @template T
 * @template K
 * @param {(params?:K, config?:AxiosRequestConfig) => AxiosPromise<T>} reqFunc
 * @param {K} [params]
 * @param {Config<T>} [config]
 * @returns {GenerateRequestHook<T>}
 */
export const generateRequest = <T, K>(reqFunc:(params?:K, config?:AxiosRequestConfig) => AxiosPromise<T>, params?:K, config?:Config<T>):GenerateRequestHook<T, K> => {
  const uid = _uid++;
  const source = axios.CancelToken.source();
  return {
    run: async function(_params?:K) {
      // eslint-disable-next-line @typescript-eslint/no-this-alias
      const self:GenerateRequestHook<T, K> = this;
      if (!self || uid !== self.uid) {
        throw Error('[generateRequest] The context exception');
      }
      try {
        self.isError = false;
        self.loading = true;
        if (_params) {
          self.params = _params;
        }
        const res = await reqFunc(_params || params, {
          cancelToken: source.token,
          ...config?.config,
        });
        // 格式化返回資料
        if (config?.format) {
          config.format(res.data);
        }
        self.data = res.data;
        return res;
      } catch (error) {
        self.data = config?.initData;
        self.isError = true;
        return Promise.reject(error);
      } finally {
        self.loading = false;
      }
    },
    data: config?.initData,
    loading: false,
    isError: false,
    uid,
    cancel: source.cancel,
    params,
  };
};

使用demo

<template>
  <div class="article_info">
  </div>
</template>
<script lang="ts">
import { Vue, Options } from 'vue-property-decorator';
import { generateRequest } from './utils';
import { getArticle } from './api';

@Options({})
export default class extends Vue {
  articleReq = generateRequest(getArticle)
  created() {
    this.articleReq.run(1).then((res) => {
      document.title = this.title;
    }).catch((err) => {
      console.error(err.message);
    });
  }
}
</script>

上面的this.articleReq會包含的屬性,我們可以直接使用loadingdataisError,而且這些屬性都是具備響應式的,而且data屬性是根據傳入getArticle推斷出資料型別,十分方便。可以通過cancel方法取消請求,run可以發起請求。

上面我的run方法內有這麼一段判斷,因為run方法內部是用了this來進行資料更新,讓屬性具備響應式。不過相對的限制就是不能修改this.articleReq.run呼叫時的上下文。這樣會導致獲取屬性異常。不過這個限制基本沒什麼影響,好處是大於壞處的。所以我給每個請求分配一個uid,用於判斷請求run方法的上下文是否一致。

if (!self || uid !== self.uid) {
    throw Error('[generateRequest] The context exception');
}

結語

上面generateRequest建立了一個具備響應式的請求例項,實現了從請求引數、請求資料都具備宣告,且可以直接在元件內使用具備響應式的屬性。目前來說還是挺方便的。

相關文章