👉 前言
防抖节流概念: < 性能优化:认识防抖与节流,如何实现呢?它们又有何区别? >
今天这篇文章,主要是讲述对axios封装的请求,由于部分请求可能存在延时的情况。使得接口可能存在会被持续点击(即:接口未响应的时间内,被持续请求),导致重复请求的问题,容易降低前后端服务的性能!
故提出给axios封装
的配置里面,新增一个防抖函数,用来限制全局请求的防抖。不过介于部分接口可能存在需要持续请求的情况,所以增加一个参数来判断是否启用防抖函数
!
👉 一、核心代码 : 防抖函数
const debounce = (fn, delay, immediate = true) => {
// 1.定义一个定时器, 保存上一次的定时器
let timeout = JSON.parse(localStorage.getItem('timeout')) || null
// window.console.log(fn, delay, timeout)
// 2.真正执行的函数
const _debounce = function() {
let context = this;
let args = arguments;
if (timeout) clearTimeout(timeout); // timeout 不为null
if (immediate) {
// 第一次会立即执行,以后只有事件执行后才会再次触发
let callNow = !timeout;
timeout = setTimeout(function() {
timeout = null;
}, delay)
if (callNow) {
fn.apply(context, args)
}
} else {
timeout = setTimeout(function() {
fn.apply(context, args)
}, delay);
}
localStorage.setItem('timeout', timeout)
}
// 封装取消功能
_debounce.cancel = function() {
if (timeout) clearTimeout(timeout)
timeout = null
localStorage.setItem('timeout', null)
}
return _debounce
}
export default debounce;
👉 二、Axios封装中的配置
// 使用axios请求
this.xxxName({
...
isDebounce: true // 入参
})
.then((res) => {
...
})
.finally(() => {});
---------------------------------------------------
import debounce from "./debounce.js";
export default function(options) {
// options 为接口入参配置,options.data为接口参数
...
let isDebounce = false;
try{
isDebounce = config.data.isDebounce;
if(isDebounce) {
delete data.isDebounce;
}
} catch(e){
//TODO handle the exception
isDebounce = false
}
return createPromise(type, url, data, config, socket, cache, isDebounce);
}
function createPromise(type, url, data, config, socket, cache = {}, isDebounce = false) {
...
return new Promise(function(resolve, reject) {
if (cacheData) {
resolve(cacheData);
} else {
const fn = () => {
// 请求接口代码
http
.then(function(res) {
...
})
.catch(function(res) {
...
reject(res);
})
.finally(() => {
debounce().cancel()
});
}
// 对axios请求进行防抖配置, 防抖时间为 5s
if(isDebounce) {
debounce(fn, 5000)()
} else {
fn()
}
}
});
}
👉 三、实现原理
介于我们封装axios
后,每次请求都会统一执行一次我们封装的函数。从而统一在函数内进行防抖判断,防止短时间发出大量误触请求!
因为小温是在项目后期进行功能优化,所以以入参的形式(默认关闭)控制是否开启防抖功能!
难点:由于防抖函数位于axios封装方法中,所以每次请求都无法获取到上次执行遗留下来的timer
。相当于每次执行的防抖函数都为新的函数,导致timer变量无法被闭包存储,无法起到请求防抖的功能! 故而通过将timer暂存至本地,解决问题!
给防抖函数增加中断防抖的功能,在请求响应后通过cancel函数
关闭当前防抖流程!
👉 结论
本篇文章仅是基于实际开发中,对防抖概念的实际应用。可能部分逻辑存在问题,需要各位小伙伴在使用时,根据实际场景做修改! 本文仅用于思路指导!在实际运用还是存在问题: 比如当存在两个及以上接口同时请求时,会使防抖函数失效,导致后请求的接口被强行取消。
这个问题也需要重点去解决! 但是本项目中,仅用于列表接口查询时的防抖,故暂不解决这个问题!
原因: 出现问题的原因也很简单, 由于 timer
只存一个参数,防抖函数无法准确的判断防抖对象, 就是同时请求的接口,后面的接口被当作第一个触发的请求接口的抖动请求(也就是防抖防错对象了),故被防抖阻止了。
解决思路: 将本地存储的数据,存储为object
类型, 按接口地址 Url
作为 key值存储 timer
,请求之前,查询当前 Url
,在本地存储中查询有没有正在执行的请求!若有,则进入防抖。反之,则执行正常请求流程!
👉 补充优化: 解决多个接口请求,拦截掉了需要的请求
> 防抖函数
const debounce = (fn, delay, url = '', immediate = true) => {
// 1.定义一个定时器, 保存上一次的定时器
let timeout = JSON.parse(localStorage.getItem(url)) || null
// window.console.log(url, timeout)
// 2.真正执行的函数
const _debounce = function() {
let context = this;
let args = arguments;
if (timeout) clearTimeout(timeout); // timeout 不为null
... 中间内容不变
localStorage.setItem(url, timeout)
}
// 封装取消功能
_debounce.cancel = function() {
let timeout_cancel = JSON.parse(localStorage.getItem(url)) || null
if (timeout_cancel) clearTimeout(timeout_cancel)
timeout = null
localStorage.removeItem(url)
}
return _debounce
}
export default debounce;
> 引用位置
新增入参 Url ,请求地址,如果怕接口信息被网站获取,导致泄漏! 可以以斜杠为分割字符串,使用sqit('/')
分割! 取最后一个作为本地存储的标记(key
)值
最后的最后,如果觉得小温这篇文章对你有所帮助,请不要吝惜你的小手,给小温三连支持一下吧~ 😘