• 设计vue3的请求实体工厂


    设计一个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创建了一个具备响应式的请求实例,实现了从请求参数、请求数据都具备声明,且可以直接在组件内使用具备响应式的属性。目前来说还是挺方便的。

  • 相关阅读:
    nginx 配置以及常用命令
    django TypeError: 'module' object is not callable
    Django Meta内部类选项
    安装MySQL后要做的事
    MySQL存储引擎
    kali linux 2018.2 mysql密码修改后无效,外部无法连接问题。
    Django中CSS加载background url('')问题
    升级OPENSSH 和 OPENSSL
    Git创建仓库的方法(github翻译)
    Git标签(版本)管理
  • 原文地址:https://www.cnblogs.com/suyuanli/p/14586405.html
Copyright © 2020-2023  润新知