封装一个好用的axios,省时又省力,真香!!

原创...2022年6月30日

简单聊一聊我在项目中是如何封装 axios 以及如何统一管理 api 接口,其主要目的是简化代码,使代码结构更清晰,便于后期更新维护。

一、初识 axios

axios - 一个易用、简洁且高效的 http 库

它具有以下几个优点

  • 支持node端和浏览器端 - 同样的 API,node和浏览器全支持,平台切换无压力
  • 支持 Promise - 使用Promise管理异步,告别传统callback方式
  • 丰富的配置项 - 支持拦截器等高级配置
  • 社区支持 - axios相关的npm包数量一直在增长

优点还挺多,上手试试。

二、封装 axios

1. 安装依赖

可以使用 npm、yarn 等进行安装,这里我使用 yarn

yarn add axios

引入axios。我一般是在src下创建一个utils文件夹,在其中新建一个request.js放置封装好的axios

import axios from "axios";

2. 创建实例

// 创建实例
const instance = axios.create();

// 创建实例后修改默认值
axios.defaults.baseURL =
  process.env.NODE_ENV == "development"
    ? "http://127.0.0.1:8081"
    : "https://api.example.com"; // 默认请求地址,需根据环境判断请求的路径
axios.defaults.timeout = 10000; // 超时时间,单位毫秒
axios.defaults.headers.post["Content-Type"] =
  "application/x-www-form-urlencoded"; // post请求头的设置

3. 请求拦截

/**
 * 请求拦截器
 * 每次请求前,如果存在token则在请求头中携带token
 */
axios.interceptors.request.use(
  (config) => {
    LoadingBar.start();
    // 添加token
    const token = getToken();
    token && (config.headers.Authorization = "Bearer " + token);
    return config;
  },
  (error) => Promise.error(error)
);

4. 响应拦截

/**
 * 响应拦截器
 * 每次请求后,判断请求状态码是否正确,及数据做处理
 */
axios.interceptors.response.use(
  /**
   * 传输层:接口正常或异常,用http状态码
   * 业务层:业务正常或异常,用自定义状态码
   */
  // 请求成功
  (res) => {
    LoadingBar.stop();
    // HTTP 状态码
    if (res.status !== 200) {
      return Promise.reject(res);
    }

    // 业务状态码
    let code = res.data.code;
    if (!code || code === 2000) {
      // 无code,则请求的是html页面;有code,则返回请求的数据
      return Promise.resolve(res.data);
    }

    errorHandle(code, res.data.msg);
    return Promise.reject(false);
  },
  // 请求失败
  (error) => {
    LoadingBar.stop();
    const { response } = error;
    if (response) {
      // 请求已发出,但是不在2xx的范围
      errorHandle(response.status, response.data.message);
      return Promise.reject(response);
    } else {
      tip("网络出现故障,请稍后再试");
    }
  }
);

5. 错误处理

/**
 * 请求失败后的错误统一处理
 * @param {Number} status 请求失败的状态码
 */
const errorHandle = (status, msg) => {
    // 状态码判断
    switch (status) {
        // 2002: 用户名/密码错误
        case 2002:
            tip('用户名或密码错误!')
            break
        // 4003: token过期,清除token并跳转登录页
        case 4003:
            toLogin("登录信息过期")
            break
        // 其他状态码
        ...
        default:
            tip('后台维护中,请稍后再试')
    }
}

/**
* 提示函数
*/
const tip = msg => {
    // 使用UI框架自带的错误弹框即可
    Vue.prototype.$msg.error(msg)
}

/**
 * 跳转登录页
 * 携带当前页面路由,以便在登录完成登录后返回当前页面
 */
const toLogin = async (msg) => {
    // 移除token、用户信息

    // 跳转登录页
    router.replace({
        path: '/login',
        query: {
            redirect: router.currentRoute.fullPath
        }
    })
}

三、使用 axios

1. 创建 api 接口

user模块为例,在src目录下新建api文件夹,用来存放项目的所有接口请求,新建user.js,代码如下:

import axios from '@/utils/request'

/**
 * @description: 用户登录
 * @param {String} username 用户名
 * @param {String} password 密码(aes加密)
 */
export const userLogin = params => {
    return axios.post('/user/login', params)
}
// 其他user接口
...

2. 在页面使用

import { userLogin } from "@/api/user";

userLogin({
  username: this.username,
  password: this.password, // 记得加密QAQ
}).then((res) => {
  this.$msg.success("登录成功");
});

四、api 进阶

很多项目都是通过以上方式来调用接口的,但是这种方式有个弊端,在每个页面都得引入需要的 API,如果某个界面用到不同模块的接口,得引入好几次,造成代码冗余。

在 B 站自学的时候,发现了一个更合适的方法,下面就来详细讲解一下。

1. 创建 api 接口

user模块作为一个整体导出。

import axios from '@/utils/request'

export const user = {
    /**
     * @description: 用户登录
     * @param {String} username 用户名
     * @param {String} password 密码(aes加密)
     */
    userLogin(params) {
        return axios.post('/user/login', params)
    }
    // 其他user接口
    ...
}

2. 创建 index.js

在使用 api 时,经常需要importexport各种模块,那么有没有什么办法可以简化这种操作呢?答案是肯定的,下面就为大家介绍一下require.context的基本使用方法:

require.context(directory,useSubdirectories,regExp),三个参数分别如下:

  • directory 要查找的文件路径
  • useSubdirectories 是否查找子目录
  • regExp 要匹配文件的正则

具体使用方式见:webpack 中 require.context 的作用open in new windowrequire.context官网地址:webpack-requirecontextopen in new window

本案例使用如下,将所有index.js同级 api 导入,导入后统一导出,最后在main.js将所有 api 挂载到vue

// 批量导出文件
const requireApi = require.context(
  // api 目录的相对路径
  ".",
  // 是否查询子目录
  false,
  // 查询文件的一个后缀
  /.js$/
);

let module = {};
requireApi.keys().forEach((key, index) => {
  if (key === "./index.js") return;
  Object.assign(module, requireApi(key));
});

export default module;

3. 引入及使用

main.js引入所有 api

import Vue from 'vue'
import api from '@/api'

Vue.prototype.$api = api
...

使用

this.$api.user.userLogin().then((res) => {
  // 接口响应成功后的一些处理...
});

五、总结

完结撒花 😁😁😁

本文首发于足各路的博客open in new window,后续会同步更新到掘金open in new window知乎open in new windowCSDNopen in new window关注足各路、前端不迷路!

上次编辑于: 2022/8/5 09:41:10
评论
Powered by Waline v2.6.2