Vue3+ts项目中axios封装

818 阅读4分钟

前言

最近为了熟悉Vue3+ts的开发,特意在项目练习时使用其进行开发,涉及到axios网络请求封装一块,确实从中学到了一些知识,特此总结一下

由于没有实习经历,文章中的封装方法仍有许多不足,希望各位读者能积极指出,互通有无,共同学习

http模块

一般封装axios都会在项目src目录新建一个http文件夹,然后创建一个request.ts进行axios封装

//我这里创建一个request类用来封装axios
class request{
    private instance:AxiosInstance;
    constructor(config:AxiosRequestConfig){
        //初始化instance,创建一个axios实例
        this.instance =axios.create(config)
        //拦截器配置(axios中一个很重要的模块)
        this.interceptors()
    }
    private interceptors(){
        ......
    }
    //拦截器配置函数和instance属性都是私有的,不向外暴漏
    
    //下面封装各类网络请求方法
    get(){....}
    post(){......}
}

拦截器配置函数

private interceptors(){
    //请求发送之前(一般的操作是要在此时配置token)(此时拦截器操作的是config)
    this.instance.interceptors.request.use((config:AxiosRequestConfig)=>{
        let token= getToken() || "" //getToken()为封装的工具函数(获取sessionStorage中的token,下面会说明)
        if(token){ //存在token就加上
            config.headers ={
                token:token
            }
        }
        return config
    },(error:any)=>{
        error.data ={};
        error.data.msg ="请求发送异常!!"
        return error
    })
//-----------------------------------------------------------------------------------------------    
    //请求返回之后的处理(一般处理后端设置的特殊状态)
    this.instance.interceptors.response.use((res:AxiosResponse)=>{
        //此为响应成功,即http请求返回的状态码为200
        if (res && res.data){
            const data =res.data;
            //此时查看后端返回的状态信息
            if (data.code ==600){
                //600仅为举例,具体情况看各自项目后端设置,比如此处600表示登录请求响应成功,但是无权限(token过期或者无token)
                router.push({path:"/login"}) //跳转到登录
            }else if (data.code ==200){
                return res
            }else{
                ElMessage.error(data.msg || "服务器出错!!") //借助element-plus组件进行错误提示
                return res || null;
            }
        }
    },error=>{
        //此处表示http响应失败,在各种项目的axios封装中都是通用的
        error.data ={};
        if (error && error.response){
            switch(error.response.status){
                case xxx:
                    err.data.msg ="xxxx";
                    ElMessage.error(error.data.msg)
                    break
                .......
                default:
                    error.data.msg =`连接错误${error.msg.status}`
                    ElMessage.error(error.data.msg)
            }
        }else {
            error.data.msg = "连接到服务器失败";
            ElMessage.error(error.data.msg)
        }
        return error;
    })
}

http响应失败的状态码判断

                    case 400:
                        error.data.msg = '错误请求';
                        ElMessage.error(error.data.msg)
                        break
                    case 401:
                        error.data.msg = '未授权,请重新登录';
                        ElMessage.error(error.data.msg)
                        break
                    case 403:
                        error.data.msg = '拒绝访问';
                        ElMessage.error(error.data.msg)
                        break
                    case 404:
                        error.data.msg = '请求错误,未找到该资源';
                        ElMessage.error(error.data.msg)
                        break
                    case 405:
                        error.data.msg = '请求方法未允许';
                        ElMessage.error(error.data.msg)
                        break
                    case 408:
                        error.data.msg = '请求超时';
                        ElMessage.error(error.data.msg)
                        break
                    case 500:
                        error.data.msg = '服务器端出错';
                        ElMessage.error(error.data.msg)
                        break
                    case 501:
                        error.data.msg = '网络未实现';
                        ElMessage.error(error.data.msg)
                        break
                    case 502:
                        error.data.msg = '网络错误';
                        ElMessage.error(error.data.msg)
                        break
                    case 503:
                        error.data.msg = '服务不可用';
                        ElMessage.error(error.data.msg)
                        break
                    case 504:
                        error.data.msg = '网络超时';
                        ElMessage.error(error.data.msg)
                        break
                    case 505:
                        error.data.msg = 'http版本不支持该请求';
                        ElMessage.error(error.data.msg)
                        break
                    default:
                        error.data.msg = `连接错误${error.response.status}`;
                        ElMessage.error(error.data.msg)

get/post请求封装

这里其实有点争议,封装与不封装都可以,封装之后固然可以更加方便的使用,但是某些具体的请求可能和封装后有区别,这个时候就不能直接使用封装后的get/post函数,可以专门针对该函数重新封装(就变麻烦了)

import qs from "qs" //
//返回结果的接口(根据自己后端返回数据定义)
interface Result<T =any> {
    code:number,
    msg:string,
    data :T
}
//Promise类型需要接收一个泛型参数(目的是为了定义响应数据的类型)
//T用于指定响应数据中data的类型
get<T =any>(url:string,params?:any):Promise<Result<T>>{
    return new Promise((resolve,reject)=>{
        //需要注意的是,当我们get请求需要携带query参数时,可以在paramsSerializer字段里指定处理函数
        //将{username:"yzh",id:2} =>http://xxxx?username=yzh&id=2
        this.instance.get<T>(url,{params:params,paramsSerializer:(params)=>{
        return qs.stringify(params)}})
        .then(res=>{resolve(res.data as any)})
        .catch(err=>{reject(err)})
    })
}

post<T =any>(url:string,params:any):Promise<Result<T>>{
    return new Promise((resolve,reject)=>{
        this.instance.post(url,parmas,{
            //转换成json格式
            transformRequest:[(params)=>{return JSON.stringify(params)}],
            headers:{"Content-type":"application/json"}
        }).then(res=>{resolve(res.data as any)})
        .catch(err=>{reject(err)})
    })
}

至此,http模块中的axios封装完成,我们将其导出,然后在http文件夹下新建http.ts文件,创建一个axios实例并导出,即可在其他需要调用axios的地方引入http.ts

http.ts

import request from "./request"
const http =new request({
    //配置一下config
    baseURL:"http://xxxxxxx",
    timeout:10000
})
export default http

api模块

为了更好的管理各种请求,我们通常在src目录下新建api文件夹,然后在api文件夹下再分模块统一管理,比如user文件夹,menu文件夹等等(这样的好处还有,每个请求可能会定义各种请求参数接口以及返回结果接口,也可以统一管理在各模块中,降低在组件请求时业务逻辑的耦合度)

这里以user模块为例

userModel.ts

//定义各种接口

//登录请求参数接口
export interface LoginParam{
    username:string,
    password:string,
    code:string
}
//登录返回结果接口
export interface LoginResult{
    id:number,
    token:string,
    code:number,
    expireTime:number
}
......

user.ts

import http from "@/http/http"
import { LoginParam, LoginResult } from "./userModel"
enum Api {
    getImg ="/api/sysUser/image",
    login ="/api/user/login",
    getInfo ="/api/sysUser/getInfo"
}

// 获取验证码
export async function getImageApi(){
    return await http.getImage(Api.getImg)
}

// 登录
export async function loginApi(params:LoginParam){
    return await http.login<LoginResult>(Api.login,params)
}

工具类函数模块

所谓工具类函数,在项目中就不一定与axios有关,不过之前封装axios时,在请求拦截器中获取token却是可以封装到工具类模块中。

enum Keys{
    Token = 'token',
    UserId = 'userId',
    ExpireTime = 'expireTime'
}
//存储token到session
export const setToken = (token:string)=>{
    sessionStorage.setItem(Keys.Token,token)
}
export const getToken = ()=>{
    return sessionStorage.getItem(Keys.Token)
}
...