本文介绍: 本文介绍了在Vue项目使用axios进行HTTP通信方法。首先详细介绍axios安装配置方法,包括npmyarn安装以及文件请求简单示例然后讲解axios拦截器的作用和使用方法,分别介绍了请求拦截器响应拦截器使用。接着讲解了对axios请求封装,包括configtypeshttp三个文件的具体内容,以及如何使用封装好的BaseRequest类进行请求最后,介绍了mockjs的作用和安装配置方法展示mock的使用测试请求

系列文章目录(点击查看)



前言

Vue项目中使用 axios 是常见的实践,因为 axios一个流行的基于PromiseHTTP 客户端,可用于浏览器Node.js 环境。它提供了许多优点,包括易用性、可靠性和在处理 HTTP 请求时的灵活性。

使用 axios 可以轻松地执行 HTTP 请求,包括 GETPOST 等,还可以简化对响应数据处理。此外,axios 还提供了拦截器(interceptors)取消请求、全局配置等功能,使其成为Vue 项目中进行 HTTP 通信的强大工具

axios 相比其他 HTTP 客户端具有几个优势:


一、安装和配置

1、安装

# npn 安装
npm install axios
# yarn 安装
yarn add axios

在这里插入图片描述

2、配置

axios 不用想其他依赖需要main.tsimport,只需要在文件中请求,就可以使用,不过为了更方便的在项目中使用,我们需要通过拦截器为 axios统一的封装,在个在下一节会说到。

src 目录创建 api 文件夹,并创建 userApi.ts 文件

import axios from "axios";

// 登陆
export function userLogin(data = {}) {
  return axios.post({
    url: "/login",
    data,
  });
}

二、拦截器介绍和使用

1、拦截器介绍

axios一个基于 PromiseHTTP 客户端,可用于浏览器Node.js。它允许您在发送请求或接收响应时使用拦截器来执行特定的操作axios 拦截器分为请求拦截器和响应拦截器。

请求拦截器允许您在发送请求之前对其进行修改添加特定的配置。您可以通过请求拦截器来添加认证信息设置请求头、转换请求数据操作

2、拦截器的使用

请求拦截器

// 添加请求拦截器
axios.interceptors.request.use(function (config) {
  // 在发送请求之前做些什么
  config.headers.Authorization = 'Bearer token'
  return config
}, function (error) {
  // 对请求错误做些什么
  return Promise.reject(error)
})

响应拦截器

// 添加响应拦截器
axios.interceptors.response.use(function (response) {
  // 对响应数据做点什么
  return response
}, function (error) {
  // 对响应错误做点什么
  return Promise.reject(error)
})

三、axios 请求封装

utils 文件下创建 service 文件夹新建四个文件,分别是:config.tshttp.ts、index.ts、types.ts

1、config.ts文件

该文件为 url 等相关配置

const TEST_BASE_URL = '/mock'
const API_BASE_URL = '/api'
const AUTH_BASE_URL = '/auth'

const TIME_OUT = 30 * 1000

export {
  TEST_BASE_URL,
  API_BASE_URL,
  AUTH_BASE_URL,
  TIME_OUT
}

2、types.ts文件

该文件为ts 的类型声明

import type { AxiosResponse, AxiosRequestConfig, InternalAxiosRequestConfig } from 'axios'

export interface BaseRequestInterceptors<T = AxiosResponse<any&gt;&gt; {
  requestInterceptor?: (config: InternalAxiosRequestConfig) => InternalAxiosRequestConfig
  requestInterceptorCatch?: (error: any) => any
  responseInterceptor?: (res: T) => T
  responseInterceptorCatch?: (error: any) => any
}

export interface BaseRequestConfig<T = AxiosResponse> extends AxiosRequestConfig {
  interceptors?: BaseRequestInterceptors<T>
  loading?: boolean | string
  noToken?: boolean
}

3、http.ts文件

该文件将请求拦截和相应拦截以及相关逻辑全部封装

定义一个基于 axios 封装的 BaseRequest 类,用于发送 http 请求。具体功能有:

  1. 创建 axios 实例,并允许用户自定义配置;
  2. 支持使用拦截器对请求和响应进行处理;
  3. 支持在请求中添加 token
  4. 支持在请求中显示 loading
  5. 提供一系列方法用于发送 getpost、put、deletepatch 请求;
  6. 对不同的 http 错误状态码进行处理,并在错误时显示对应的错误信息
import axios from "axios";
import type { AxiosInstance, InternalAxiosRequestConfig } from "axios";
import { BaseRequestConfig, BaseRequestInterceptors } from "./types";

import { ElLoading, ElMessage, ElMessageBox } from "element-plus";
import { LoadingInstance } from "element-plus/es/components/loading/src/loading";
import { getToken, logout } from "@/utils/tools/user";

const DEFAULT_LOADING = false;
let isRelogin = false;

class BaseRequest {
  // axios 实例
  instance: AxiosInstance;
  interceptors?: BaseRequestInterceptors;
  showLoading: boolean | string;
  loading?: LoadingInstance;

  constructor(config: BaseRequestConfig) {
    this.instance = axios.create(config);
    this.showLoading = config.loading ?? DEFAULT_LOADING;
    this.interceptors = config.interceptors;

    // 使用拦截器
    // 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) => {
        if (config.params &amp;&amp; config.params.responseType) {
          config.responseType = config.params.responseType;
          delete config.params.responseType;
        }
        if (
          (config.method === "post" || config.method === "put") &amp;&amp;
          config.params
        ) {
          if (config.params.formData) {
            config.data = config.params.formData;
          } else {
            config.data = { ...config.params };
          }
          delete config.params;
        }
        // console.log('所有的实例都有的拦截器: 请求成功拦截')
        if (this.showLoading) {
          const loadingText =
            typeof this.showLoading == "boolean" ? "加载中" : this.showLoading;
          this.loading = ElLoading.service({
            lock: true,
            text: loadingText + "...",
            background: "rgba(0, 0, 0, 0.5)",
          });
        }
        return config;
      },
      (err) => {
        console.log("所有的实例都有的拦截器: 请求失败拦截");
        return err;
      }
    );

    this.instance.interceptors.response.use(
      (res) => {
        // 将loading移除
        this.loading?.close();
        if (res.status !== 200) return res;
        return res.data;
      },
      (err) => {
        // console.log('所有的实例都有的拦截器: 响应失败拦截')
        // 将loading移除
        this.loading?.close();

        // 例子: 判断不同的HttpErrorCode显示不同的错误信息
        switch (err.response.status) {
          case 400:
            ElMessage.error("请求错误(400)");
            break;
          case 401:
            ElMessage.error("未授权,请重新登录(401)");
            logout();
            break;
          case 403:
            ElMessage.error("拒绝访问(403)");
            break;
          case 404:
            ElMessage.error("请求出错(404)");
            break;
          case 408:
            ElMessage.error("请求超时(408)");
            break;
          case 500:
            ElMessage.error("服务器错误(500)");
            break;
          case 501:
            ElMessage.error("服务实现(501)");
            break;
          case 502:
            ElMessage.error("网络错误(502)");
            break;
          case 503:
            ElMessage.error("服务不可用(503)");
            break;
          case 504:
            ElMessage.error("网络超时(504)");
            break;
          case 505:
            ElMessage.error("HTTP版本不受支持(505)");
            break;
          default: {
            ElMessage.error(`连接出错(${err.response.status})!`);
          }
        }
        return err;
      }
    );
  }

  request<T = any>(config: BaseRequestConfig<T>): Promise<T> {
    return new Promise((resolve, reject) => {
      // 1.单个请求对请求config的处理
      if (config.interceptors?.requestInterceptor) {
        // 对config 进行转化
        config = config.interceptors.requestInterceptor(
          config as InternalAxiosRequestConfig
        );
      }
      // 携带token的拦截
      if (!config.noToken &amp;&amp; getToken()) {
        config.headers = {
          Authorization: getToken(),
        };
      }
      // 2.判断是否需要显示loading
      if (config.loading) {
        this.showLoading = config.loading;
      }
      this.instance
        .request<any, T>(config)
        .then((res: any) => {
          // 1.单个请求对数据的处理
          if (config.interceptors?.responseInterceptor) {
            res = config.interceptors.responseInterceptor(res);
          }
          // 2.将showLoading设置true, 这样不会影响一个请求
          this.showLoading = DEFAULT_LOADING;
          if (res.status !== 200) {
            if (config.responseType == "blob") {
              resolve(res);
              return;
            }
            let errMsg = res.message || "请求失败";
            if (res.code == "501") errMsg = "数据存在敏感词汇, 请修改!";
            if (res.status == 401) {
              if (!isRelogin) {
                isRelogin = true;
                // prettier-ignore
                errMsg = res.message || '登录状态过期,您可以继续留在该页面,或者重新登录'
                ElMessageBox.confirm(errMsg, "系统提示", {
                  confirmButtonText: "重新登录",
                  cancelButtonText: "取消",
                  type: "warning",
                })
                  .then(() => {
                    isRelogin = false;
                    logout();
                  })
                  .catch(() => {
                    isRelogin = false;
                  });
              }
              reject(res);
              return;
            }
            ElMessage({
              message: errMsg,
              type: "error",
            });
            reject(res);
          } else {
            resolve(res);
          }
        })
        .catch((err) => {
          // 将showLoading设置true, 这样不会影响一个请求
          this.showLoading = DEFAULT_LOADING;
          reject(err);
          return err;
        });
    });
  }

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

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

  put<T = any>(config: BaseRequestConfig<T>): Promise<T> {
    return this.request<T>({ ...config, method: "PUT" });
  }

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

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

export default BaseRequest;

4、index.ts文件

使用已经封装好的 BaseRequest

import BaseRequest from './http'
import { TIME_OUT } from './config'

const testRequest = new BaseRequest({
  timeout: TIME_OUT,
  interceptors: {
    requestInterceptor: config => {
      return config
    },
    requestInterceptorCatch: err => {
      // console.log('请求失败的拦截')
      return err
    },
    responseInterceptor: res => {
      // console.log('响应成功的拦截')
      return res
    },
    responseInterceptorCatch: err => {
      const errRes = err.response
      if (errRes) {
        return {
          status: errRes.status,
          message: errRes.data.message
        }
      }
      return err
    }
  }
})

export default testRequest

5、使用封装

修改 login 接口

import testRequest from "@/utils/service";
import { TEST_BASE_URL } from "@/utils/service/config";

// 登陆
export function userLogin(data = {}) {
  return testRequest.post({
    url: TEST_BASE_URL + "/login",
    data,
  });
}

四、mock 的使用

1、什么是 mockjs

Mock.js一个用于生成模拟数据JavaScript 库,它能帮助前端开发人员模拟后端接口数据,尤其适用于前后端分离开发中。通过 Mock.js开发人员可以轻松地创建和配置虚拟接口,从而使前端能够在后端接口尚未完成或不易获取时进行开发测试

Mock.js 提供了丰富的数据模拟功能,包括生成随机文本数字日期、布尔值、数组等等,还能模拟不同情况下的数据返回。使用 Mock.js 可以大大提高前端开发效率,促进前后端协作,是一个强大而实用的前端开发工具

2、安装

# yarn 安装
yarn add mockjs -D

# 这里要特别注意版本,如果版本过高会报错 require 找不到
yarn add vite-plugin-mock@2.9.6 -D 

在这里插入图片描述

3、配置

根目录创建 mock 文件,并在其中创建 index.ts 文件,增加测试的请求

import { MockMethod } from "vite-plugin-mock";

const mockItems: MockMethod[] = [
  {
    url: "/mock/login",
    method: "post",
    response: () => {
      return {
        status: 200,
        data: {
          "access_token": "abc",
        },
        message: "success",
      };
    },
  },
];

export default mockItems;

vite.config.ts 文件中

import { viteMockServe } from "vite-plugin-mock";

plugins: [
      vue(),
      nodePolyfills(),
      setupExtend(),
      // 新增代码
      viteMockServe({
        mockPath: "./mock",
        localEnabled: command === "serve"
      }),
      // ...
]

4、界面测试 mock

  1. 修改登录按钮逻辑

views文件下 login文件登录逻辑修改如下

const loginClick = () => {
  loading.value = true;
  const accessToken = getToken();
  if (!accessToken) {
    userLogin()
      .then((res) => {
        setToken(res.data.access_token);
      })
      .then(() => {
        userLoginFunc();
      })
      .catch((err) => {
        ElMessage.error(err);
        loading.value = false;
      });
  } else {
    userLoginFunc();
  }
};
  1. 登录页面点击登录触发接口
    在这里插入图片描述
    在这里插入图片描述

总结

本文介绍了在 Vue 项目中使用 axios 进行 HTTP 通信方法。首先详细介绍了 axios 的安装和配置方法,包括 npmyarn 安装以及文件请求的简单示例然后讲解axios 拦截器的作用和使用方法,分别介绍了请求拦截器和响应拦截器的使用。接着讲解了对 axios 请求的封装,包括 configtypeshttp 三个文件的具体内容,以及如何使用封装好的 BaseRequest 类进行请求。最后,介绍了 mockjs 的作用和安装配置方法,展示mock 的使用测试请求。上文中的配置代码可在 github 仓库中直接 copy仓库路径https://github.com/SmallTeddy/testing-web

原文地址:https://blog.csdn.net/SmallTeddy/article/details/134551347

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任

如若转载,请注明出处:http://www.7code.cn/show_13877.html

如若内容造成侵权/违法违规/事实不符,请联系代码007邮箱suwngjj01@126.com进行投诉反馈,一经查实,立即删除

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注