目录
前言
项目中有一些接口调用第三方的时候相应会很慢,会出现短时间内重复请求
还有用户在短时间内多次提交数据
都会产生并发冲突这种在前端可以做一次拦截
目前我总结的有两种方法:
第一种方案:在提交确认按钮上添加loading等待
第二种方案:在axios请求做统一的接口重复提交拦截
下面具体说明两种情况怎么使用
提交确认按钮上添加loading等待
这种方式比较简单,主要就是平时的编码习惯,在.vue页面请求接口的时候在请求之前添加loading:true,接口调用成功后将loading:false,这也是比较简单而且粗暴的解决方案,可谓白猫黑猫,抓到老鼠就是好猫
项目中实际应用:
axios请求添加统一拦截
项目中出现并发的情况比较多,当我们项目做到一定的程度的时候,要是按照上面添加loading的方式去解决并发问题显然已经不是最优的解决方案了
接口每次的请求都会出现并发的情况,如果我们的项目中axios是统一进行二次封装的,那就可以在统一请求的地方添加请求拦截,自动处理重复请求,这样也是大大优化了我们的业务代码
axios发送http请求的时候官方有两个核心的拦截请求的API:
1.interceptors
n. 截击舰,[航][军] 截击机;拦截器;拦截者(interceptor的复数)
拦截器有两种拦截器(请求拦截和相应拦截)我们可以在这两个地方去做处理
项目中实际应用:
2.cancel token
:
调用 cancel token API
能够取消请求。官网提供了两种方式来构建 cancel token
,咱们采用这种方式:经过传递一个 executor
函数到 CancelToken
的构造函数来建立 cancel token
,方便在上面的请求拦截器中检测到重复请求能够当即执行:
let cancel
config.cancelToken = new axios.CancelToken(function (c) {
cancel = c
})
request请求的全部代码
import axios from 'axios'
import { getToken } from '_utils/token'
export default class httpRequest {
constructor (baseUrl = '') {
this.baseUrl = baseUrl
// 存储请求队列
this.queue = []
}
getInsideConfig () {
const config = {
baseURL: this.baseUrl,
// 请求超时时间
timeout: 10 * 1000,
headers: {
'Content-Type': 'application/json; charset=utf-8',
'X-URL-PATH': location.pathname
},
}
return config
}
// 对特殊配置要求做配置修改--可以针对特殊接口添加配置
changeConfig (options) {
let tempOptions = options
return tempOptions
}
// 销毁请求实例
destroy (config) {
let matchIndex = findMatchIndex(this.queue, config)
if (matchIndex !== -1) {
this.queue.splice(matchIndex, 1) //接口请求成功后在请求队列的数组中删除掉
}
LoadingBar.finish()
store.dict.state.loading = false
}
// 请求拦截
interceptors (instance, url, method) {
// 添加请求拦截器
instance.interceptors.request.use(
config => {
if (config.isLoading) {
// 这里可以添加全局loading... 可以将接口的状态添加在store中全局使用
}
// 处理重复请求
let cancel
config.cancelToken = new axios.CancelToken(function (c) {
cancel = c
})
let matchIndex = findMatchIndex(this.queue, config)
if (matchIndex !== -1) {
Notice.error({
title: '提示',
desc: '请不要重复提交'
})
cancel('请求中断')
} else {
//判断接口没有重复请求的时候,将接口添加到请求队列中
this.queue.push({
url: config.url,
method: config.method,
params: config.params
})
}
// 请求发送前处理.
config.headers.authorization = getToken()
return config
},
error => {
// 请求错误处理
return Promise.reject(error)
}
)
// 添加响应拦截器
instance.interceptors.response.use(
res => {
let { config } = res
if (config.isLoading) {
// 请求响应后销毁请求实例
this.destroy(config)
}
let { data } = res
// code 验证
let { code } = data
let { state } = data
if (code === 200) {
// 无权限
if (code === 'code_400') {
return Promise.reject(data)
}
// 错误401 token无效 删除token 返回鉴权中心(登录页)
if (code === 401) {
logoff()
// 其他错误 处理
}
if (code === 'code_500') {
return Promise.reject(data)
}
if (code === 'code_406') { //请求参数不正确!
return Promise.reject(data)
}
}
if (state === '500') { //服务器错误
}
return data
},
error => {
LoadingBar.error()
// 响应错误处理
return Promise.reject(error)
}
)
}
request (options) {
const instance = axios.create()
// 合并options
options = this.changeConfig(Object.assign(this.getInsideConfig(), options))
// 注册拦截器
this.interceptors(instance, options.url, options.method)
// 返回实例
return instance(options)
}
}
function findMatchIndex (map, config) {
return map.findIndex(item => {
if (
config.url.includes(item.url) &&
item.method === config.method &&
diff(item.params, config.params)
) {
}
return (
config.url.includes(item.url) &&
item.method === config.method &&
diff(item.params, config.params)
)
})
}
function diff (obj1, obj2) {
if (Object.keys(obj1).length !== Object.keys(obj2).length) {
return false
}
let obj1Keys = Object.keys(obj1)
for (let i = 0; i < obj1Keys.length; i++) {
if (obj1[obj1Keys[i]] !== obj2[obj1Keys[i]]) {
return false
}
}
return true
}
总结
并发请求比较常见,在接口请求出统一处理,但是在具体的业务中,出现接口请求时间比较长的接口还是要通过全局的loading状态去控制提交数据时的按钮,按钮上添加loading或者disable掉,这个根据自己的业务具体对待