节流并不能完全避免重复请求的问题,通过loading的方式需要挂钩具体的业务代码这两种方式这里就不讲了
方案一:注册自定义防抖指令避免一定时间内的重复请求
原理:利用防抖函数结合vue提供的自定义指令API directive实现
防抖原理:在一定时间内多次触发、只会执行最后一次。通俗点讲,不管触发多少次事件,都会等到事件触发n秒后才执行、如果在n秒内再次触发,那么就以新的事件的时间为准重新计算
自定义指令文件 custom-shell.ts
let timer: ReturnType<typeof setTimeout>
// 防抖函数
function debounce (binding?: any, time?: number) {
let fn: any = null
timer && clearTimeout(timer)
timer = setTimeout(() => {
if (isType(binding.value) === 'Function') {
fn = binding.value
}
if (isType(binding.value) === 'Object' && isType(binding.value.fn) === 'Function') {
fn = binding.value.fn
}
fn(binding.value.params)
}, time);
}
let customShells: shellType[] = [
{
name: 'debounce',
desc: '防抖指令',
comments: `
传参方式:
1.v-debounce:time = 函数 例如: v-debounce:2000 = "function"
方法1适用于只修改时间节点使用
2. v-debounce = "{ fn: “function”, time: 1000, params: {}}"
方法2适用所有情况: fn 触发函数 time: 触发的时间节点 params: 触发函数需要使用的参数
`,
shell: {
mounted: (el: HTMLElement, binding: any) => {
let time = 1000
if (isType(binding.arg * 1) === 'Number') {
time = binding.arg * 1
}
if (isType(binding.value) === 'Object' && binding.value.time) {
time = binding.value.time * 1
}
el.addEventListener('click', debounce.bind(null, binding, time))
},
beforeUnmount: (el: HTMLElement) => {
el.removeEventListener('click', debounce)
}
}
},
]
在main.ts文件下注册
// 引入指令文件
import customShells from '@uilts/uilts-custom-shell'
// 创建实例
const app = createApp(App)
// 注册自定义指令
customShells.forEach((item: shellType) => {
app.directive(item.name, item.shell)
})
方案二:利用axios拦截器、取消重复请求
原理:维护一个请求队列padding、在axios请求拦截中判断本次请求的API是否在请求队列中、若不存在,添加进队列,若存在,更新该请求数据。在响应拦截器中、当API成功返回时,将本次请求移出请求队列。当响应报错时,清空队列(主动取消请求也会抛出ERR_CANCELED错误、需要额外处理)
// 创建axios实例(import.meta.env 我的配置文件 根据你们项目自行修改)
const httpService = axios.create({
withCredentials: false, // 允许携带cookie
baseURL: import.meta.env.config_baseUrl,
timeout: import.meta.env.config_timeOut,
//修改请求头信息
headers: {
'Content-Type': 'application/json;charset=UTF-8'
},
});
// 请求存储对象
let padding: Map<string, any> = new Map()
// 清空请求队列
function clearPadding () {
padding.clear()
}
// 移除请求队列项
function removePadding (requeryUrl: string) {
if (padding.has(requeryUrl)) {
padding.delete(requeryUrl)
}
}
// 判断是否需要取消请求
function isRequery (requeryUrl: string, cancelFun?: any) {
if (padding.has(requeryUrl)) {
// 请求重复时 取消上一次的请求
padding.get(requeryUrl).abort('取消重复请求')
}
// 更新请求队列
padding.set(requeryUrl, cancelFun)
}
// 请求拦截
httpService.interceptors.request.use(
(req: objectType<any>) => {
// neverCancel控制是否允许重复请求
if (!req.neverCancel) {
// 不允许重复请求
let abortController = new AbortController()
// 设置请求标识
req.signal = abortController.signal
// 判断是否需要取消请求
isRequery(req.url, abortController)
}
return req
}
);
// 响应拦截
httpService.interceptors.response.use(
(res: any) => {
// 请求响应了 不管是成功还是失败 都要将本次请求移除队列
removePadding(res.config.url)
// 返回响应信息
return res.data
},
(error: any) => {
// padding 数据唯一 当是主动取消请求的报错 不需要移除原请求
if (error.code !== 'ERR_CANCELED') {
clearPadding()
}
return Promise.reject(error)
}
);
// 部分代码摘出来了 可根据自身业务拓展
纯代码、有其他想法欢迎交流。