问题描述

现在我们的项目需要升级,技术栈为Vue3+TypeScript。所以,现在我需要使用TS对axios进行重新封装

封装目标

  • 调用接口时,只需要确定url和method
  • 出现多个请求地址时,通过修改.env类似文件即可,无需修改源代码

封装过程

1.安装axios

使用npm安装axios依赖

npm install axios

在这里插入图片描述

2.实现基本封装

  • 首先导入axios
  • 创造构造器
  • 设置request
import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios';

class Request {
    private instance: AxiosInstance | undefined

    constructor(config: AxiosRequestConfig) {
        this.instance = axios.create(config)
    }
    request(config: AxiosRequestConfig): Promise<AxiosResponse> {
        return new Promise<AxiosResponse>((resolve, reject) => {
            this.instance?.request(config)
                .then((res) => {
                    resolve(res)
                })
                .catch((err) => {
                    reject(err)
                })
        })
    }
}

将其封装为一个类,而不是一个函数。
因为类可以创建多个实例,也就是说可以访问完全不同的服务器的接口。

其中可能出现的问题
Cannot find module ‘axios;‘ or its corresponding type declarations.ts(2307)

3.创建实例

我想通过不同的运行环境创建不同的实例,所以设置如下
创建.env文件
根据vue-cli 模式与环境变量官方写到,可根据env后指定mode即可创建不同的请求路径
创建.env.development——用于开发环境

NODE_ENV = development
VUE_APP_BASE_URL = "http://你的开发请求的地址"

创建.env.production——用于正式部署环境

NODE_ENV = production
VUE_APP_BASE_URL = "http://正式部署时请求的地址"

创建实例
因为我根据不同环境去设置变量时候,地址名字都是VUE_APP_BASE_RUL所以,可以根据此去创建axios请求的baseUrl

import Request from "./request";
/**
 * process.env.VUE_APP_BASE_URL 根据NODE_ENV变化而变化
 */
const web: Request = new Request({
    baseURL: process.env.VUE_APP_BASE_URL,
})

export default web

现在尝试在main.ts中调用一个接口

import web from './utils/request/index'

web.request({
    url: '/login',
})
    .then((res) => {
        console.log(res);
    })

在这里插入图片描述

4.添加响应和请求拦截器

现在对request.ts进行优化升级(添加拦截器+泛型——复用性)

import axios, { AxiosInstance, AxiosRequestConfig } from 'axios';
// import type { AxiosInstance , AxiosResponse } from 'axios';

class Request {
    private instance: AxiosInstance | undefined

    constructor(requeseConfig: AxiosRequestConfig) {
        this.instance = axios.create(requeseConfig)

        // 全局请求拦截
        this.instance.interceptors.request.use(
            (config) => {
                console.log("全局请求拦截的", config);
                return config
            },
            (error) => {
                console.log("全局请求拦截失败", error);
            },
        )

        // 全局响应拦截
        this.instance.interceptors.response.use(
            (res) => {
                // res 为AxiosResponse 类型,含有conig\data\headers\request\status\statusText属性
                console.log("全局响应拦截的", res);
                return res.data
                // 只需要返回data即可
            },
            (error) => {
                console.log("全局响应失败拦截");
                console.log(error.request);
                console.log(error.response);
                return error
            },
        )
    }

    request<T>(config: AxiosRequestConfig<T>): Promise<T> {
        return new Promise<T>((resolve, reject) => {
            /* eslint-disable */
            this.instance?.request<any, T>(config)
                .then((res) => {
                    resolve(res)
                })
                .catch((err) => {
                    reject(err)
                })
        })
    }
}

export default Request

再次在main.ts中调用

web.request({
    url: '/login',
})
    .then((res) => {
        console.log(res);
    })
    .catch((err) => {
        console.log(err);
    })

现在的结果
在这里插入图片描述

5.在请求头中添加token

在创建的index.ts中,添加token

import Request from "./request";
/**
 * process.env.VUE_APP_BASE_URL 根据NODE_ENV变化而变化
 */

/* eslint-disable */
const token = String(window.localStorage.getItem('token'))
const web: Request = new Request({
    baseURL: process.env.VUE_APP_BASE_URL,
    timeout: process.env.VUE_APP_TIME_OUT,
    headers: {
        'Content-Type': 'application/json',
        'Accept': "application/json",
        'Authorization': token,
    },
})

export default web

现在的请求结果
在这里插入图片描述
现在请求的接口,带上了token,便于后端进行权限控制

6.封装GET,POST方法

get(url: string) {
        return new Promise((resolve, reject) => {
            this.instance?.post(url)
                .then((res) => {
                    resolve(res.data)
                })
                .catch((err) => {
                    reject(err.data)
                })
        })
    }

    post(url: string, data = {}) {
        return new Promise((resolve, reject) => {
            this.instance?.post(url, data)
                .then((res) => {
                    resolve(res.data)
                })
                .catch((err) => {
                    reject(err.data)
                })
        })
    }

就是在这里,我自闭了好久根本搞不懂别人是怎么做的。
因为一开始我根本对axios和ts都没有一个理解和认知,所以对于更加抽象的封装就显得无能为力了。
然后我就开始尝试看之前我们项目的代码结合其他开源项目的操作,就想到直接传urldata并给data一个初始值。

7.定义对应后端的Ts的接口

因为我希望对项目进行模块化的封装,于是创建了专门封装接口的api文件夹,文件夹下根据模块新建文件夹。
这里以user模块下的login为例
在api模块下,我创建了models.ts,里面存放所有与后端对应接口的类型接口
例如:
后端的登录接口可以接受参数login包括

  • 邮箱:string
  • 密码:string
  • 验证码:string
  • 时间错:string

于是在userModels.ts下定义接口如下

// 登录接口
export interface login {
    accountEmail: string,
    accountPassword: string,
    code: string,
    data: string,
}

8.实现接口的封装

同样的继续创建login.ts文件,去实现接口
代码如下:

import web from "@/utils/request";
import { login } from "../models";

export default function login(requesrData?: login) {
    const responesData = web.post('/login', requesrData)
    return responesData;
}
  • 因为后端可以不传入任何参数所以使用?:去定义类型
    • 当有参数时,遵守login接口类型
    • 当没有参数时,直接进入即可

9.开始调用接口

在对应的页面进行调用接口

<template>
    <button @click="login">来吧,试试一个接口看看</button>
</template>

<script setup lang='ts'>
import login from '@/api/user/login';

const a = 1;
console.log(a);

const login= async () => {
    await login({
        accountEmail: "xx@qq.com",
        accountPassword: "xxxxxxxxxxxxxx",
        code: "2xl4",
        data: "1647152454419",
}

</script>

<style scoped>
</style>

若是不满足定义的类型,则会报错
请添加图片描述

但是!!!这里注意的是
虽然静态报错,但是依旧可以运行成功!!!,暂时还没有找到解决办法阻断运行


总结

运行成功~
请添加图片描述
成功的那一刻是喜悦的!!!毕竟这算是自己独立封装出来的
虽然可能一些可拓展性存在缺陷,但终究结果也是可以接受的。这次用TS去封装axios有点快要自闭的感觉,自己对这些东西理解不深,然后看那些为了可扩展性和高复用性进行高度抽象的封装,就有些不知所措了,而且在这个过程中,自己没有及时的去真正尝试去理解。

而是只有当真正走投无路的时候,才开始去尝试其他的方法,有点过度依赖博客的感觉!

最后成功其实是借鉴了一些类似Vue3+TS的开源项目的封装思想。

所以这也提醒了我一个问题,当在百度中找不到你想要答案的时候,去看看其他人同类型的项目,获取就会豁然开朗

毕竟,一个程序员都是从借鉴学习开始的!

Logo

前往低代码交流专区

更多推荐