axios的二次封装以及扩展功能

3,308 阅读5分钟

axios二次封装

在项目中使用axios时通常需要二次封装,比如添加请求头,或者统一处理响应状态

设置默认配置

  1. 引入axios
$ npm install axios
  1. 导入到项目中

在src目录下创建一个request文件夹,request文件夹下创建request.js封装我们的axios

// 引入axios库
import axios from 'axios'
  1. 使用axios提供的方法创建实例

axios提供了一个create方法,里面可以传入配置项,配置我们的axios默认的属性

  • baseURL: 请求的url前缀

通常我们发请求,前面的域名都是一样的,每次都要写就会很麻烦,比如:127.0.0.1/get 127.0.0.1/post 所以我们把前面的域名抽取出来,axios在每次发请求的时候就会帮我们把baseURL拼接上

  • timeout: 请求时间

当我们后端接口发生错误时,或者前端接口写错了等等情况,请求得不到响应时,timeout会在设置的时间过了后提示我们发送的请求超时了

  • responseType: 数据格式

responseType表示服务器响应的数据类型

// 通过create方法创建实例
const request = axios.create({
    baseURL: '127.0.0.1' // 请求url的默认前缀,
    timeout: 30000,
    responseType: 'json'
})

设置请求拦截器

请求拦截器中可以在Reaquet Headers中添加上后端需要的属性

// 请求拦截器
request.interceptors.request.use(
  (config) => {
    // config 中有我们请求的信息
    // 获取请求的url地址
    const url = config.url
    // 从缓存中读取token值
    const token = localStorage.getItem('token')
    // 当我们缓存中有token则把token添加到请求头中,携带到后端
    if (token) {
      config.headers.token = token
    }
    return config
  },
  (error) => {
    return Promise.reject(new Error(error))

设置响应拦截器

响应拦截器中,可以拿到后端返回的数据,因为axios的响应数据是经过内部封装的对象,里面有很多属性我们是不需要的,我们只想看到后端返回的数据。通过返回的状态码,给用户提示信息或者返回数据,弹窗提示等等,状态码可以提前跟后端协商好,比如200为请求成功,401为权限不足等等......

request.interceptors.response.use(
  (res) => {
    // 响应统一处理
    const status = res.data.code || 200
    const message = res.data.message || '未知错误'
    if (status === 401) {
      // 路由跳转
      alert('权限不足')
      return Promise.reject(new Error(message))
    }
    if (status !== 200) {
      alert('错误码' + status + '    ' + message)
      return Promise.reject(new Error(message))
    }
    return res.data
  },
  (error) => {
    return Promise.reject(new Error(error))
  }
)

最后把我们封装好的axios导出

export default request

添加减少重复请求功能

再创建一个js文件,导入我们二次封装好的axios实例

分析需求:

  • 当客户发送请求时,可能会频繁触发多次,那么要避免这种情况的发生
  • 每次发请求前,把请求的url地址保存到一个数组中,请求完成时删除该url地址,每次请求前查看数组中是否有该url地址,如果有则不发送请求
  1. 引入实例对象
import request from './request/request.js'
  1. 这里使用到自执行函数(后面会说原因)
const myRequest = (function () {
  // 存储历史请求url
  let hasRequest = []
  return function (config) {
    // 或者当前请求url地址
    let url = config.url
    // 如果发起重复请求则忽略
    if (hasRequest.includes(url)) {
      return Promise.reject({ msg: '请求已提交' })
    }
    // 把请求的url放在请求历史记录里
    hasRequest.push(url)
    return request(config).then((res) => {
      // 请求完成后筛选已完成的请求
      hasRequest = hasRequest.filter((item) => item !== url)
      // 把数组返回
      return res
    })
  }
})()

缓存请求数据

可能有些数据并不会经常更新,这里用到了用空间换时间的思想,利用闭包的机制把数据存在缓存中,当我们发送了请求后把数据存到缓存中,再下一次请求,查看当前缓存有没有改url地址,如果有则不发送请求,直接从缓存中拿取数据返回,那么可以加快获取数据的时间,但是缓存在浏览器中无法释放

需求分析:

  • 这里建议使用Map存放数据,Map中提供了很多便捷的方法操作数据
  • 调用我们二次封装的axios实例发送请求,把数据存放到Map
  • 每一次请求,通过Map.get(url)判断当前url是否存在
const myRequest = (function () {
  // 存储历史请求url
  let hasRequest = []
  // 缓存返回结果
  let memery = new Map()
  return function (config) {
    let url = config.url
    // 如果对象中存在url则返回对应的value
    if (memery.has(url)) {
      return Promise.resolve(memery.get(url))
    }
    // 如果发起重复请求则忽略
    if (hasRequest.includes(url)) {
      return Promise.reject({ msg: '请求已提交' })
    }
    // 把请求的url放在请求历史记录里
    hasRequest.push(url)
    return request(config).then((res) => {
      // 请求完成后筛选已完成的请求
      hasRequest = hasRequest.filter((item) => item !== url)
      // 保存返回结果
      memery.set(url, res)
      return res
    })
  }
})()

最后我们可以返回两个实例对象,供后期开发使用,因为我们并不会所有数据都放到缓存中,如果需要缓存则调用扩展的这个方法就可以

export { request as initRequest, myRequest as request } // initRequest表示初始的 // request表示扩展的

这里为什么要使用自执行函数呢,在Vue初始化的时候,所有文件都会执行一次,使用自执行函数在初始化时就已经执行了,我们每次调用的时候,调用的是返回出来的函数,返回的函数对外部作用域的hasRequestmemery两个变量保持了引用,那么函数内的数据是无法被释放的,利用闭包的机制实现了我们数据缓存的需求,如果不了解的话可以看一下闭包的原理

如何调用

如果需要缓存数据,则要通过传配置项的形式去调用

<script>
import { request } from '@/api/request'
export default {
  name: 'HelloWorld',
  props: {
    msg: String
  },
  created() {},
  methods: {
    get() {
      request({
        url: '/test'
      })
        .then((res) => {
          console.log(res)
        })
        .catch((err, message) => {
          console.log(err, message)
        })
    }
  }
}
</script>

如果使用普通的请求,用哪种方式都可以

<script>
import { initRequest } from '@/api/request'
export default {
  name: 'HelloWorld',
  props: {
    msg: String
  },
  created() {},
  methods: {
    get() {
      initRequest.get('/test')
        .then((res) => {
          console.log(res)
        })
        .catch((err, message) => {
          console.log(err, message)
        })
    }
  }
}
</script>