vue3+typescript+axios+Element-plus进阶封装方案
布依前端
2021年11月01日 15:55
收录于文集
共4篇

之前已经有一篇关于ts+axios的封装方法,但是那个封装不是利用纯ts方法封装的,略带有点瑕疵。仅用此文补充和参考,如果用ts文件,强烈建议用此文章封装方法。反之用js+axios即可。

通过本文你将学到:

  1. typescript的class类进行封装axios;

  2. ts接口类型判断定义;

  3. get请求参数序列化;

  4. axios.CancelToken取消重复请求方法使用;

  5. AxiosRequestConfig和AxiosInstance 类型使用

  6. 学会ts类实现接口方法;

具体实验步骤:

1、http.ts文件封装axios函数库

代码块
JavaScript
自动换行
复制代码
import axios, { AxiosInstance, AxiosRequestConfig } from 'axios';
import { ElMessage } from 'element-plus';

// 接口类型和方法
interface BaseType{
    baseURL:string;
    getConfigParams():any;
    interceptors(instance:AxiosInstance, url: string | number | undefined):any;
    request(options: AxiosRequestConfig):any;
}

interface AxiosRequestType {
    baseURL?: string;
    url?: string|undefined;
    data?: any;
    params?: any;
    method?: string;
    headers?: any;
    timeout?: number;
    value?: any
    cancelToken?: any
}

// 取消重复请求
const CancelToken = axios.CancelToken
// 用于存储每个请求的取消函数以及对应标识
let sources:any = []

// 取消函数
let removeSource = (config:any)=>{
    for(let item in sources){
        if(sources[item].umet === config.url+"&"+config.method){
            sources[item].cancel('已取消重复请求,请勿重复请求')
            sources.splice(item, 1)
        }
    }
}

class AxiosHttpRequest implements BaseType{
    baseURL: string;
    timeout: number;
    constructor(){
        this.baseURL = import.meta.env.VITE_APP_BASE_API
        this.timeout = 1500
    }
    // 配置参数
    getConfigParams(){
        const config = {
            baseURL: this.baseURL,
            timeout: this.timeout,
            headers:{}
        }
        return config;
    }
    // 拦截设置
    interceptors(instance:AxiosInstance, url: string | number | undefined){
        // 请求拦截
        instance.interceptors.request.use((config: AxiosRequestType) =>{
            // 取消重复请求
            removeSource(config)
            config.cancelToken = new CancelToken(c =>{
                // 将取消函数存起来
                sources.push({ umet: config.url+'&'+config.method, cancel: c })
            })
            // 添加全局的loading..
            // 请求头携带token
            // config.headers['Authorization'] = 'Bearer ' + 'token666'
            config.headers['Content-Type'] = 'application/json;charset=utf-8'
            // get请求映射params参数
            if (config.method === 'get' && config.params) {
                let url = config.url + '?';
                for (const propName of Object.keys(config.params)) {
                    const value = config.params[propName];
                    var part = encodeURIComponent(propName) + "=";
                    if (value !== null && typeof(value) !== "undefined") {
                        if (typeof value === 'object') {
                            for (const key of Object.keys(value)) {
                                let params = propName + '[' + key + ']';
                                var subPart = encodeURIComponent(params) + "=";
                                url += subPart + encodeURIComponent(value[key]) + "&";
                            }
                        } else {
                            url += part + encodeURIComponent(value) + "&";
                        }
                    }
                }
                url = url.slice(0, -1);
                config.params = {};
                config.url = url;
            }
            return config
        }, (error:any)=>{
            return Promise.reject(error)
        })

        // 响应拦截
        instance.interceptors.response.use((res:any) =>{
            // 取消重复请求
            removeSource(res.config)
            
            // 未设置状态码则默认成功状态
            const code = res.data['code'] || 200;
            // 获取错误信息
            let msg = res.data['msg'] || ""
            switch (code) {
                case "401":
                    msg = '认证失败,无法访问系统资源'
                    break;
                case "403":
                    msg = '当前操作没有权限'
                    break;
                case "404":
                    msg = '访问资源不存在'
                    break;
                case "default":
                    msg = '系统未知错误,请反馈给管理员'
                    break;
                default:
                    return '未知错误,请联系管理员'
            }
            if(code === 200){
                return Promise.resolve(res.data)
            }else{
                ElMessage.error(msg)
                return Promise.reject(res.data)
            }
        }, (error:any) =>{
            console.log('err' + error)
            let { message } = error;
            if (message == "Network Error") {
                message = "后端接口连接异常";
            }
            else if (message.includes("timeout")) {
                message = "系统接口请求超时";
            }
            else if (message.includes("Request failed with status code")) {
                message = "系统接口" + message.substr(message.length - 3) + "异常";
            }
            ElMessage.error({
                message: message,
                duration: 5 * 1000
            })
            return Promise.reject(error)
        })
    }

    /**
     * 外部调用方法
     * @param options axios请求参数
     * @returns 实例
     */
    request(options: AxiosRequestConfig){
        const instance = axios.create();
        options = Object.assign(this.getConfigParams(), options)
        this.interceptors(instance, options.url)
        return instance(options)
    }
}

// 实例化请求类
const http = new AxiosHttpRequest();

export default http;
复制成功

2、引用axios工具函数request

代码块
JavaScript
自动换行
复制代码
import http from "@/script/utils/http";

export const getArticleList = (data:object) =>{
    return http.request({
        url: '/app/blog/article/page',
        method: 'POST',
        data
    })
}
复制成功

3、vue文件内引用api示例

代码块
JavaScript
自动换行
复制代码
<script lang="ts" setup>
import {onMounted} from "vue";
import { getArticleList } from "@/script/api/article";
import PageParams from '@/script/utils/page-params';

const pageParam = reactive<PageParams>({
	pageNum: 1,
	pageSize: 10
})

onMounted(()=>{
	getArticleList(pageParam).then(res=>{
		console.log(res);
	})
})
</script>
复制成功

备注:具体response响应回调判断要根据自己服务接口返回类型判断。这里我是以我的服务器响应数据格式为案例的。