噢!这是最清晰最实用的Axios最佳实践讲解了吧

引言

不从需求出发的二次封装都是技术流氓

axios的出现是为了帮助前端开发者更加方便的完成前后端数据交互,不同的业务需求对axios做二次封装的设计是不同的。本篇文章主要讲一讲各场景下的二次封装实现思路。

不从需求出发的二次封装都是技术流氓 不做为了封装而封装的事情、不做为了装杯而封装的事情

Axios 是基于 Ajax 的第三方库,它诞生的初衷就是为了更加方便的实现前后端数据交互。但 Axios 并不是直接继承自传统的 XMLHttpRequest(XHR)对象,而是基于 Promise 的一个现代库,它对 XHR 进行了封装,并提供了更简洁的 API 以及额外的功能。

二次封装的目的是基于它提供的简洁的API用法,进一步针对性的把业务中通用的部分内置进去,减少后期的代码量,降低维护成本。

通用的基本配置

这一部分都不能算作是封装,只是axios的基本使用。

axios提供了许多用于请求的方法,这里不一一列出,就以post()get()为例。

如下图,我准备了三个接口,这三个接口啥事不做就只是为了前端请求用。

前端请求接口如果直接使用axios提供的get()post()方法,那势必如下面这样

js 复制代码
import axios from 'axios'
const request1=()=>{
  axios.get('http://192.168.3.91:3000/course/list')
    .then(res => {

    })
}
const request2=()=>{
  axios.get('http://192.168.3.91:3000/model/list')
    .then(res => {
      
    })
}
const request3=()=>{
  axios.post('http://192.168.3.91:3000/course/create',{
    name:'书苑书斋'
  })
    .then(res => {
      
    })
}

这样直接调用axios提供的请求方法唯一的好处就是简单明了,但是通常在实际项目中不会直接这样使用。在不修改axios默认配置的情况下,它的缺点是很明显的:

  • 每次都要写完整的服务地址,基础服务地址无法公用
  • 无法统一配置超时时间,统一设置请求头等
  • 无法配置请求、响应拦截,对返回值做统一处理

当然你可以通过 axios.defaults.baseURL修改默认配置,但这依然不是推荐的方案

创建实例配置全局

axios 还提供了一个create()方法用来创建一个axios实例,创建实例的方式在项目中是普遍使用,也是推荐使用的。在创建实例的过程中允许传入一个config配置项,用于设置服务基础地址超时时间请求头跨域凭证等。

js 复制代码
export const request = axios.create({
  baseURL:'http://192.168.3.91:3000',   // 基础服务地址,这个值会被添加到实际请求 URL 的前面
  timeout:5000, // 超时时间 单位是毫秒
  headers:{ // 请求头设置 可以设置全局或特定的headers
    'Content-Type':'application/json;charset=utf-8'
  },
  withCredentials:true, //是否携带 cookie 发送跨域请求。默认为 false
})

创建axios实例的配置常用的就以上四个,还有其它的都不常用。

这样就可以用request实例调用请求方法了,用这个request实例调用的请求方法都会继承使用创建实例时的配置项,后需要修改基础服务地址或其他都只需更改一个地方即可。

js 复制代码
request.get('/course/list').then(res=>{
    console.log(res)
})

为什么需要配置响应拦截器

发起请求,此时观察控制台输出的内容你会看到控制台输出的内容可能并不是你希望的。

它返回的对象中包含了如上许多属性对象,但其实在业务代码中我们只关注后端响应回来的data 数据,并不需要关心发送请求时使用的config配置对象是什么,并且对status HTTP状态码不为200的也会做统一的异常处理。因此,需要设置响应拦截器

最简单的响应拦截,从response中取出业务代码需要的data并且返回

js 复制代码
request.interceptors.response.use(
  response => {
    return response.data
  }
)

那么此时在控制台输出打印的就一定是data的属性值了。

请求异常,统一处理 响应拦截器还接收第二个回调参数,在请求发生异常时会执行这个回调参数。通常会在这个异常回调中根据Http状态码统一处理异常时的逻辑。

举例 现在将两个接口的返回状态码分别设置成401403,服务端接口如下图:

然后分别请求这两个接口,我们希望的是当请求异常时,在响应拦截器中就直接做出错误提示了,如下动图效果:

这一部分的统一处理就在相应拦截中统一做,代码如下所示

js 复制代码
request.interceptors.response.use(
  response => {
    console.log(response)
    return response.data
  },
  error=>{
    let message = ''
    if (error && error.response) {
      switch (error.response.status) {
        case 401:
          message = '未登录,请先登录'
          break;
        case 403:
          message = '您没有权限操作!'
          break;
      }
    }
    ElMessage({ type: 'error', message, duration:1000 })
  }
)

请求拦截其中的身份令牌

前后端数据交互,通常需要权限的接口都要求前端在发起请求的时候在请求头上携带一个token去请求后端接口,后端拿到token就可以校验用户身份。

在请求拦截其中最基本的事情就是把token设置到请求头headers上,比如:

js 复制代码
const token = sessionStorage.getItem('token')
token && (config.headers.Authorization = token)

请求拦截中的参数加密

参数加密通常为了安全性考虑请求的参数也不是明文请求,你打开控制台也无法查看请求参数是什么,这样一定程度上防止了黑客通过请求参数分析你的接口,如下图:

前端发送给后端的是这样一串经过加密算法加密的字符串,后端会用同一种算法对这串字符进行解密。

参数加密这一步也是在请求拦截器中完成,例如请求接口,对参数进行加密,后端接收到解密后直接返回解密后的数据,展示在界面上,效果如下图:

实现参数加密,只需要在请求拦截器中对data参数重写即可

js 复制代码
request.interceptors.request.use(
  config => {
    config.data = { data: encrypt(JSON.stringify(config.data)) }
    return config
  }
)

axios进阶使用

以上是一个前端开发者必须掌握的axios基础技能,只是axios的基本配置使用。

如果项目中有部分请求需要参数加密,有部分请求不需要参数加密。再比如经常困扰前端的一个点击按钮重复提交的问题等,这些都可以在上面的基础上进行扩展解决。

首先在上面基础上可以做一点点修改,不直接在代码中使用request.get('/course/list')的这种方式发送请求。而是将所有的请求用一个单独的api文件来维护,比如像这样:

那么在页面代码中就可以直接调用对应的接口方法

js 复制代码
getCourseList().then(res => {
    console.log(res)
})

自定义参数

这里的自定义参数指的是配置像是参数是否加密、如何处理响应结果等。这些自定义的参数完全是根据你自己的项目去制定的,我这里以参数是否加密为例。

已知我们已经创建了axios实例,也可以在请求头中处理参数加密,但是所有使用这个axios实例的请求都会对参数进行加密。我们需要一个参数来控制,那可以创建一个函数,在函数中返回这个axios实例,同时这个函数接收自定义参数。

如下:只需要加上一条if语句控制即可

js 复制代码
function createInstance(axiosConfig,customConfig){
  const request = axios.create({
    baseURL:'http://192.168.3.91:3000',   // 基础服务地址,这个值会被添加到实际请求 URL 的前面
    timeout:60 * 1000 * 2, // 超时时间 单位是毫秒
    headers:{ // 请求头设置 可以设置全局或特定的headers
      'Content-Type':'application/json;charset=utf-8'
    }
  })
  const customOpt = Object.assign(
    {
      isCrypto: false, // 是否开启加密,默认true
    },
    customConfig
  )
  request.interceptors.request.use(
    config => {
      if(customOpt.isCrypto){
        config.data = { data: encrypt(JSON.stringify(config.data)) }
      }
      return config
    }
  )
  // 请求拦截 响应拦截省略
  ...
  return request(axiosConfig)
}

更多自定义参数可自行在cursomOpt配置对象中添加默认值。

防止重复提交

我们不希望用户暴力点击,不希望在前要给请求的结果还没有返回,又不断的发送同一个请求。我们希望同一个请求,只有在上一次请求结果回来之后才能再次发送。

思考:要解决暴力点击的问题,你觉得使用防抖的思路可以吗?

这里我们从axios入手,思路很简单:创建一个集合,当请求进来之后先在集合中找这个请求存不存在,如果存在则代表请求已经发送了,则不执行发送请求的操作;如果不存在则先把这个请求塞到集合中,继续发送请求操作,只有当请求的结果返回才把这个请求从集合中删除掉(不管结果是成功还是异常都要删除

js 复制代码
const server = (()=> {
  let history = []
  return function(config){
    const { url } = config
    if(history.includes(url)){
      return Promise.reject({ msg:'请求已提交' })
    }
    history.push(url)
    return request({
      ...config
    }).finally(()=>{
      const idx = history.indexOf(url)
      history.splice(idx,1)
    })
  }
})()

最终效果如下图:


其它精选文章:

《超详细》前端环境变量原理解析

相关推荐
DT——1 小时前
Vite项目中eslint的简单配置
前端·javascript·代码规范
mingzhi612 小时前
网安面试会问到的:http的长连接和短连接
http·面试·职场和发展
极客先躯2 小时前
高级java每日一道面试题-2024年9月16日-框架篇-Spring MVC和Struts的区别是什么?
java·spring·面试·mvc·struts2·框架篇·高级java
学习ing小白3 小时前
JavaWeb - 5 - 前端工程化
前端·elementui·vue
真的很上进4 小时前
【Git必看系列】—— Git巨好用的神器之git stash篇
java·前端·javascript·数据结构·git·react.js
胖虎哥er4 小时前
Html&Css 基础总结(基础好了才是最能打的)三
前端·css·html
qq_278063714 小时前
css scrollbar-width: none 隐藏默认滚动条
开发语言·前端·javascript
.ccl4 小时前
web开发 之 HTML、CSS、JavaScript、以及JavaScript的高级框架Vue(学习版2)
前端·javascript·vue.js
小徐不会写代码4 小时前
vue 实现tab菜单切换
前端·javascript·vue.js
2301_765347544 小时前
Vue3 Day7-全局组件、指令以及pinia
前端·javascript·vue.js