在开发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集合中的数据。
这样处理保证了同时发出了多次请求,只会处理一次响应结果,但是这样并没有减轻服务器的压力,多次请求还是被服务器所处理,只是前端只处理一次响应结果而已。
该方案在百度上学习到的
实现代码如下:
- 定义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涉及到的知识点,如果还有遗漏的地方希望在评论区指出。最后祝大家端午安康,粽子很香。