vue项目中axios的使用心得

在开发vue项目的过程中,axios经常会作为项目中的http库。公司项目大多会选择在jeecg-boot框架上进行二次开发,里面有一套较为成熟的axios使用方案,结合我自己的使用心得,做一个vue2.x版本的axios使用心得总结。

axios封装要点

单例模式

项目中绝大多数的请求具有相同的baseURL、响应超时时间等公共配置,所以只需要使用axios.create创建一次实例,同时也优化了性能。

拦截器

1. 拦截request

  • token参数携带
  • get请求避免缓存
  • 加入请求集合,处理同时发出多个请求的情况
  • 表单类型的请求时,需要对参数进行序列化

2.拦截response

  • 简化响应结构
  • 统一错误状态码处理

封装工具函数

  • 封装get,post,put,delete四种公用函数

  • 封装支持传入这四种请求方式的公用函数

代码演示

单例模式

使用axios.create创建一个实例,之后的所有操作都是在这个实例的基础上进行操作的。

// 创建实例
const service = axios.create({
  // 公共请求头
  baseURL: apiBaseUrl,
  // 请求超时时间
  timeout: 9000 
})
// 相关拦截器处理
service...
// 导出实例
export {
  service as axios
}
复制代码

拦截器

1. 拦截request

  • token参数携带

从localStorage或者sessionStorage本地存储里取出token,在每次请求时放在headers里。

至于到底token是存储在ls还是session可以看看我之前的文章。

如果某个请求不能在headers里带上token,可以通过config.url进行排除。

service.interceptors.request.use(config => {
  const token = Vue.ls.get(ACCESS_TOKEN)
  if (token) {
    config.headers[ 'X-Access-Token' ] = token 
  }
  return config
},(error) => {
  return Promise.reject(error)
})
复制代码
  • get请求避免缓存

浏览器会对url链接相同的GET请求进行缓存,这会导致某些请求拿不到最新的数据。在GET请求里加上时间戳来解决这个问题。这里的示例代码除了字典请求之外的其他请求都加上了时间戳,根据项目实际情况可进行修改。

service.interceptors.request.use(config => {
  if(config.method=='get'){
    if(config.url.indexOf("sys/dict/getDictItems")<0){
      config.params = {
        _t: Date.parse(new Date())/1000,
        ...config.params
      }
    }
  }
  return config
},(error) => {
  return Promise.reject(error)
})
复制代码
  • 取消重复请求的实现代码稍微复杂一点,放在文章末尾进行仔细分析

  • 表单类型的请求,需要对参数进行序列化

当Axios实例设置header的content-type为``application/x-www-form-urlencoded,需要在拦截器中序列化参数:config.params=qs.stringify(parmas)。因为项目中全是json格式的请求,所以此处就不上代码了。

2.拦截response

  • 简化响应结构

axios会将请求返回的值封装在AxiosResponse.data里,如果不在拦截器中进行处理的话,每一次请求都需要对结果进行提取,代码就很冗余。

service.interceptors.response.use((response) => {
    return response.data
  }, (error) => {
  return Promise.reject(error)
})
复制代码
  • 统一错误状态码处理

一般来说,后台返回请求的格式是有规定的,不同的响应状态码表示不同的含义。例如401表示未授权、404表示资源不存在、500表示处理出现错误。那么我们可以在拦截器中统一处理这些错误情况。

const errHandler = (error) => {
  if (error.response) {
    switch (error.response.status) {
      case 401:
       notification.error({
          message: '系统提示',
          description: '未授权,请重新登录',
          duration: 4
        })
        break
      case 404:
        ...
        break
      case 500:
        ...
        break
      default:
        notification.error({
          message: '系统提示',
          description: data.message,
          duration: 4
        })
        break
    }
  }
  return Promise.reject(error)
};

service.interceptors.response.use((response) => {
    return response.data
  }, errHandler)
复制代码

封装工具函数

  • 封装get,post,put,delete四种公用函数

    封装成工具函数方便调用,以封装GET请求为例。注意这里getAction函数的返回值是AxiosPromise类型。

    import { axios } from '@/utils/request'
    
    export function getAction(url,parameter) {
      return axios({
        url: url,
        method: 'get',
        params: parameter
      })
    }
    
    // 使用举例
    getAction('sys/dict/getDictItems').then(res => {
        console.log(res)
    })
    复制代码
  • 封装支持传入这四种请求方式的公用函数

在表单提交时,如果id存在表示修改,没有表示新增,这和mybatis提供的saveOrUpdate方法类似。所以也封装一个支持四种请求的函数。这里需要注意的是:axios请求方式不同,参数名称不同。GET和DELETE使用params传参,POST和PUT使用data传参。

export function httpAction(url,parameter,method) {
    //这里判断是params还是data
  let paramOrData=['GET','DELETE'].some(item=>item===method.toUpperCase())?'params':'data'
  return axios({
    url: url,
    method:method,
    [paramOrData]: parameter
  })
}

//使用举例
let url = this.url.add, method = 'post'
if (this.model.id) {
    url = this.url.edit
    method = 'put'
}
httpAction(url, formData, method).then((res) => {
     console.log(res)
})
复制代码

如何避免同时段重复请求

当同时触发多次相同请求时,会收到多次响应,这就可能会导致出现页面多次重绘等问题。那么前端如何解决这个问题呢?

首先定义一个装Axios提供的Canceler的Map集合

在request拦截中遇到已存在的相同请求则使用Canceler进行取消,然后将发出的当前请求装在Map里,

在response拦截中移出Map集合中的数据。

这样处理保证了同时发出了多次请求,只会处理一次响应结果,但是这样并没有减轻服务器的压力,多次请求还是被服务器所处理,只是前端只处理一次响应结果而已

该方案在百度上学习到的

实现代码如下:

  1. 定义Canceler工具类
let pendingMap = new Map();

const getPendingUrl = (config) => [config.method, config.url].join('&');

export const removePending=(config)=> {
  const url = getPendingUrl(config);

  if (pendingMap.has(url)) {
    const cancel = pendingMap.get(url);
    cancel && cancel(url);
    pendingMap.delete(url);
  }
}

export const addPending=(config)=> {
  removePending(config);
  const url = getPendingUrl(config);
  config.cancelToken =new axios.CancelToken((cancel) => {
    if (!pendingMap.has(url)) {
      pendingMap.set(url, cancel);
    }
  });
}
复制代码

在axios拦截器中进行使用

service.interceptors.request.use(config => {
  ...
  addPending(config)
  return config
},(error) => {
  return Promise.reject(error)
})

service.interceptors.response.use((response) => {
    ...
    removePending(response.config)
    return response.data
  }, (error) => {
  return Promise.reject(error)
})
复制代码

如果某些请求不需要这样处理,可以通过url进行排除。

这大概就是项目中使用Axios涉及到的知识点,如果还有遗漏的地方希望在评论区指出。最后祝大家端午安康,粽子很香。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享