vue中防止切换路由接口重复请求实现方法
vue中防止切换路由接口重复请求实现方法
·
背景:这次使用vue3搭建的项目,依照组件化的思想进行了项目开发,后来发现一个问题,由于多个页面调用同一个接口服务,在进行路由切换之后,当前页再调用这个共用的接口服务时候,该接口服务会被重复调用,且切换多少次路由,就会重复调用多少次接口服务。通常项目中在每个请求接口的方法中添加一个请求锁,防止重复请求,但是这样比较low,代码比较冗余,于是添加一个全局的方法,就不用每次请求接口前手动编写请求锁,减少编码和工作量。
在进行服务调用中,由于前一次的请求耗时远大于后一次请求,导致页面渲染内容错误,本应该渲染最后一次的结果却被第一次请求的结果覆盖。对此,有很多解决方案:
- 防抖节流:可一定程度上缓解问题,但不能完全解决,请求时间过长还是会出现这个问题。
- 前后端配合,前台发请求时带上一个字段,后台返回结果时将该字段返回,前台判断只有最后一次请求时的数据为准,该方法能解决问题,但前后台都需要参与且代码量不小,维护成本高。
- 利用观察者模式,将请求顺序入栈,然后按顺序出栈。此方法可以彻底解决问题,而且也不需要后台配合,但是性能损耗大,因为需要一直监听栈的变化,而且前端维护量大,有需要的地方都需要引入。
但是上述方案都不太适合,总要牺牲一些其他方面的效率。
综上,目前最合适的解决方案如下:
原理:相同请求在没有返回值之前,若再次调用则进行拦截并取消。
在使用axios的文件中引入如下方法(这里我是将axios及限制接口重复请求方法都放在了request.js文件中):
(1)定义移除方法
// 先定义方法,用于移除重复请求
const pending = {};
const removePending = (key, isRequest = false) => {
if (pending[key] && isRequest) {
pending[key]("取消重复请求!");
}
if (pending[key]) {
delete pending[key];
}
}
(2)请求-响应拦截
import axios, { CancelToken } from 'axios'; // 引入axios的第三方插件、取消重复token
// 1. 创建axios实例
const service = axios.create({
...
})
// 2. 使用创建的axios实例生成请求拦截器
// 我这里key为 url + & + method, 可以根据项目需求自定义key键
// 每一个请求都为其创建一个key和CancelToken的实例
service.interceptors.request.use(
config => {
const key = config.url + '&' + config.method;
removePending(key, true);
config.cancelToken = new CancelToken((cancel) => {
pending[key] = cancel;
})
return config;
}
)
// 3. 使用创建的axios实例生成响应拦截器
// 若在pending中存在,则会直接cancel请求
service.interceptors.response.use(
response => {
const key = response.config.url + '&' + response.config.method;
removePending(key);
return response;
},
error => {
console.log('err' + error) // for debug
}
)
(3)完整代码
import axios, { CancelToken } from "axios" // 引入axios的第三方插件
import qs from 'qs'
import configapi from '../../public/config'
// 先定义方法,用于移除重复请求
const pending = {};
const removePending = (key, isRequest = false) => {
if (pending[key] && isRequest) {
pending[key]("取消重复请求!")
}
if (pending[key]) {
delete pending[key]
}
}
// create an axios instance
// 1. 创建axios实例
const service = axios.create({
// 公共接口绑定
baseURL: configapi.backstageIp,
// baseURL: '/api',
// 设置接口请求超市时间
timeout: 135000,
// retry: 1, // 请求次数
// retryDelay: 1000 // 请求间隙
})
// request interceptor
// 2. 使用创建的axios实例生成请求拦截器
// 我这里key为 url + & + method, 可以根据项目需求自定义key键
// 每一个请求都为其创建一个key和CancelToken的实例
service.interceptors.request.use(
config => {
// // 打开加载窗口
// Loading.open()
// 在发送请求之前带上一些东西,config是请求的配置对象,如果直接返回就等于什么都不带
// let token = window.localStorage.token
// if (token) {
// config.headers.Authorization = 'Bearer' + token //如果token 存在,就带上token
// } else {
// config.headers['token'] = '' //return
// }
const key = config.url + '&' + config.method;
removePending(key, true);
config.cancelToken = new CancelToken((cancel) => {
pending[key] = cancel;
})
if (config.method === 'post') {
config.paramsSerializer = function(params) {
return qs.stringify(params, { arrayFormat: 'repeat' })
}
// config.params = qs.stringify(config.params, { indices: false });
}
return config
},
error => {
// // 关闭加载窗口
// Loading.close()
// do something with request error
// console.log(error) // for debug
return Promise.reject(error) // 请求错误处理
}
)
// response interceptor
// 3. 使用创建的axios实例生成响应拦截器
// 若在pending中存在,则会直接cancel请求
service.interceptors.response.use(
/**
* If you want to get http information such as headers or status
* Please return response => response
*/
/**
* Determine the request status by custom code
* Here is just an example
* You can also judge the status by HTTP Status Code
*/
response => {
// // 0.7 秒后关闭加载窗口
// setTimeout(() => {
// Loading.close()
// }, 700)
const key = response.config.url + '&' + response.config.method;
removePending(key)
return response.data
},
error => {
// // 关闭加载窗口
// Loading.close()
// console.log('err' + error)
// Message({
// message: error.message,
// type: 'error',
// duration: 5 * 1000
// })
return Promise.reject(error)
}
)
export default service
更多推荐
已为社区贡献8条内容
所有评论(0)