Skip to content

axios 请求方式

  • axios(config)
  • axios.request(config)
  • axios.get(url[, config])
  • axios.delete(url[, config])
  • axios.head(url[, config])
  • axios.post(url[, data[, config]])
  • axios.put(url[, data[, config]])
  • axios.patch(url[, data[, config]])
js
import axios from 'axios'

// axios的实例对象
// get请求
axios.get('http://123.207.32.32:8000/home/multidata').then((res) => {
  console.log(res.data)
})

// 额外补充的Promise中类型的使用
// Promise本身是可以有类型
new Promise<string>((resolve) => {
  // 泛型指定了,只能传string
  resolve('abc')
}).then((res) => {
  // 并且res也是string类型的
  console.log(res.length)
}

// 使用http://httpbin.org模拟数据请求

// get请求,并且传入参数
axios
  .get('http://httpbin.org/get', {
    // get请求使用params传参,并且最后会拼接到url后面
    params: {
      name: 'coderwhy',
      age: 18
    }
  })
  .then((res) => {
    console.log(res.data)
  })

// post请求,传入参数
axios
  .post('http://httpbin.org/post', {
    // post请求使用data传参
    data: {
      name: 'why',
      age: 18
    }
  })
  .then((res) => {
    console.log(res.data)
  })
  • 同时使用两个请求
js
// axios.all -> 多个请求, 一起返回
axios
  .all([
    axios.get("/get", { params: { name: "why", age: 18 } }),
    axios.post("/post", { data: { name: "why", age: 18 } }),
  ])
  .then((res) => {
    // 结果是个数组
    console.log(res[0].data);
    console.log(res[1].data);
  });

封装 axios

首先要安装 axios

js
npm install axios
  • 项目结构
txt
├─index.ts

├─config
│ index.ts

└─request
index.ts
type.ts

配置的目的

  • 可以对某个请求、某个请求实例的所有请求、所有请求实例的所有请求,设置拦截和是否显示 loading。

配置 config.ts

js
// 根据process.env.NODE_ENV区分
// 开发环境: development
// 生成环境: production
// 测试环境: test

let BASE_URL = "";
const TIME_OUT = 10000;

if (process.env.NODE_ENV === "development") {
  BASE_URL = "http://123.207.32.32:8000/";
} else if (process.env.NODE_ENV === "production") {
  BASE_URL = "http://coderwhy.org/prod";
} else {
  BASE_URL = "http://coderwhy.org/test";
}

export { BASE_URL, TIME_OUT };

配置 type.ts

  • 用于规定创建请求实例或者调用 request 方法的时候传入的参数是什么样的
js
import type { AxiosRequestConfig, AxiosResponse } from "axios";

// 定义一个接口,表示这个接口的实例要有这4个属性,当然不是必须的,是可选的
// 传入一个泛型,默认值是AxiosResponse
export interface HYRequestInterceptors<T = AxiosResponse> {
  // 拦截器都是可选的
  // 请求拦截
  requestInterceptor?: (config: AxiosRequestConfig) => AxiosRequestConfig;
  // 请求错误拦截
  requestInterceptorCatch?: (error: any) => any;
  // 响应拦截
  // 由于我们在前面直接将res.data返回了,所以这里如果传入了T,那么返回的类型就是传入的T
  responseInterceptor?: (res: T) => T;
  // 响应错误拦截
  responseInterceptorCatch?: (error: any) => any;
}

// 定义一个新的接口,继承于AxiosRequestConfig,表示我们传入的参数要有interceptors和showLoading,当然也是可选的
export interface HYRequestConfig<T = AxiosResponse> extends AxiosRequestConfig {
  // 对原来的AxiosRequestConfig进行扩展,添加拦截器和是否显示loading,可选的
  interceptors?: HYRequestInterceptors<T>;
  showLoading?: boolean;
}

核心 request/index.js

ts
import axios from "axios";
// 导入axios实例的类型
import type { AxiosInstance } from "axios";
import type { HYRequestInterceptors, HYRequestConfig } from "./type";

// 引入loading组件
import { ElLoading } from "element-plus";
// 引入loading组件的类型
import { ILoadingInstance } from "element-plus/lib/el-loading/src/loading.type";

// 默认显示loading
const DEAFULT_LOADING = true;

class HYRequest {
  // axios实例
  instance: AxiosInstance;
  // 当前请求实例的拦截器
  interceptors?: HYRequestInterceptors;
  // 是否显示loading
  showLoading: boolean;
  // 保存的loading实例
  loading?: ILoadingInstance;

  constructor(config: HYRequestConfig) {
    // 创建axios实例
    this.instance = axios.create(config);
    // 保存基本信息
    this.interceptors = config.interceptors;
    this.showLoading = config.showLoading ?? DEAFULT_LOADING;

    // 使用拦截器
    // 1.从config中取出的拦截器是对应的实例的拦截器
    this.instance.interceptors.request.use(
      this.interceptors?.requestInterceptor,
      this.interceptors?.requestInterceptorCatch
    );
    this.instance.interceptors.response.use(
      this.interceptors?.responseInterceptor,
      this.interceptors?.responseInterceptorCatch
    );

    // 2.添加所有的实例都有的拦截器
    // 请求的时候,先添加的拦截器后执行
    // 响应的时候,先添加的拦截器先执行
    this.instance.interceptors.request.use(
      (config) => {
        console.log("所有的实例都有的拦截器: 请求成功拦截");

        // 所有的请求都添加loading
        if (this.showLoading) {
          // 添加loading
          this.loading = ElLoading.service({
            lock: true,
            text: "正在请求数据....",
            background: "rgba(0, 0, 0, 0.5)",
          });
        }
        return config;
      },
      (err) => {
        console.log("所有的实例都有的拦截器: 请求失败拦截");
        return err;
      }
    );

    this.instance.interceptors.response.use(
      (res) => {
        console.log("所有的实例都有的拦截器: 响应成功拦截");
        // 所有的请求,将loading移除
        this.loading?.close();

        // 因为我们需要的就是res.data,所以我们可以在所有请求实例的请求的响应拦截器里面,直接把res.data返回,这样我们就可以直接使用了
        const data = res.data;
        // 判断当HttpErrorCode是200的时候,服务端和客户端一块自定义的错误信息
        if (data.returnCode === "-1001") {
          console.log("请求失败~, 错误信息");
        } else {
          return data;
        }
      },
      (err) => {
        console.log("所有的实例都有的拦截器: 响应失败拦截");
        // 所有的请求,将loading移除
        this.loading?.close();

        // 判断不同的HttpErrorCode显示不同的错误信息
        if (err.response.status === 404) {
          console.log("404的错误~");
        }
        return err;
      }
    );
  }

  // 1.传入返回结果的类型T,这样在Promise中我们就知道返回值的类型是T了
  // 2.通过HYRequestConfig<T>,将返回值类型T告诉接口,从而在接口的返回响应拦截中指明返回值类型就是T
  request<T>(config: HYRequestConfig<T>): Promise<T> {
    // 返回一个Promise对象,好让使用者在外面拿到数据
    return new Promise((resolve, reject) => {
      // 1.单个请求对请求config的处理
      if (config.interceptors?.requestInterceptor) {
        // 如果有单个请求的拦截器,就执行一下这个函数,然后返回
        config = config.interceptors.requestInterceptor(config);
      }

      // 2.判断单个请求是否需要显示loading
      if (config.showLoading === false) {
        this.showLoading = config.showLoading;
      }

      this.instance
        // request里面有两个泛型,第一个泛型默认是any,第二个泛型是AxiosResponse
        // 由于前面我们已经将res.data直接返回了,所以其实最后的数据就是T类型的,所以我们在第二个泛型中要指定返回值的类型T
        .request<any, T>(config)
        .then((res) => {
          // 1.单个请求对数据的处理
          if (config.interceptors?.responseInterceptor) {
            res = config.interceptors.responseInterceptor(res);
          }
          // 2.将showLoading设置true, 这样不会影响下一个请求
          this.showLoading = DEAFULT_LOADING;

          // 3.将结果resolve返回出去
          resolve(res);
        })
        .catch((err) => {
          // 将showLoading设置true, 这样不会影响下一个请求
          this.showLoading = DEAFULT_LOADING;
          reject(err);
          return err;
        });
    });
  }

  get<T>(config: HYRequestConfig<T>): Promise<T> {
    return this.request<T>({ ...config, method: "GET" });
  }

  post<T>(config: HYRequestConfig<T>): Promise<T> {
    return this.request<T>({ ...config, method: "POST" });
  }

  delete<T>(config: HYRequestConfig<T>): Promise<T> {
    return this.request<T>({ ...config, method: "DELETE" });
  }

  patch<T>(config: HYRequestConfig<T>): Promise<T> {
    return this.request<T>({ ...config, method: "PATCH" });
  }
}

export default HYRequest;

外层 index.ts

js
// service统一出口
import HYRequest from "./request";
import { BASE_URL, TIME_OUT } from "./request/config";

// 创建一个新的请求,并传入参数
const hyRequest = new HYRequest({
  // 传入baseurl
  baseURL: BASE_URL,
  // 传入超时时间
  timeout: TIME_OUT,
  // 传入拦截器
  interceptors: {
    requestInterceptor: (config) => {
      // 给当前请求实例所有的请求添加token
      const token = "";
      if (token) {
        // 模板字符串进行拼接
        config.headers.Authorization = `Bearer ${token}`;
      }

      console.log("请求成功的拦截");
      return config;
    },
    requestInterceptorCatch: (err) => {
      console.log("请求失败的拦截");
      return err;
    },
    responseInterceptor: (res) => {
      console.log("响应成功的拦截");
      return res;
    },
    responseInterceptorCatch: (err) => {
      console.log("响应失败的拦截");
      return err;
    },
  },
});

export default hyRequest;

请求拦截

js
interceptors: {
  requestSuccessFn: (config) => {
    // 每一个请求都自动携带token
    const token = localCache.getCache(LOGIN_TOKEN);
    if (config.headers && token) {
      // 类型缩小
      config.headers.Authorization = "Bearer " + token;
    }
    return config;
  };
}