在移动端开发中,存在这样一种交互方式–下拉刷新,下拉刷新一般用于刷新当前页,从而获取最新的数据。但是在实际使用过程中,用户习惯快速重复的进行下拉的操作。如果用户每进行一次下拉操作都发起请求,这无疑会非常浪费带宽,而且许多刷新是无效刷新,即返回的数据与当前原有的数据相同。这种场景是可以进行优化的,通过使用防抖函数,在用户第一次操作下拉刷新的时候触发,之后的n秒(1秒或2秒)不再触发下拉刷新的回调函数。
什么是防抖:所谓防抖,就是指触发事件 n 秒后才执行回调函数,如果在这 n 秒内又触发了事件,则重新设置定时器为n秒后执行回调函数。
使用场景:页面中的按钮连续点击触发事件、下拉刷新、input延迟验证等。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>性能优化-防抖</title>
<style>
#app {
width: 100px;
height: 100px;
background-color: rgb(144, 165, 48);
color: white;
text-align: center;
line-height: 100px;
}
</style>
</head>
<body>
<div id="app">点我</div>
<script>
/**
* 防抖函数
* @param {Function} fn 事件对应的回调函数
* @param {Number} wait 延迟执行时间
* @param {Boolean} immediate 是否立即执行
* @returns
*/
function debounce(fn, wait, immediate = false) {
let timer = null
return function () {
const self = this, args = arguments;
// 清除之前定时器
if (timer) clearTimeout(timer)
// 是否立即执行
if (immediate) {
// 如果还在处于等待时间内,则不执行回调函数,并重新计时
let callNow = !timer
timer = setTimeout(() => {
timer = null
}, wait)
// 超过了等待时间,则可以直接执行
if (callNow) fn.call(self, args)
} else {
// 延迟执行
timer = setTimeout(() => {
fn.call(self, args)
}, wait)
}
}
}
let count = 0
const app = document.getElementById('app')
//不能使用箭头函数,否则通过防抖函数后,this会出现问题
function mouseMove() {
count++
app.textContent = count
}
app.onmousemove = debounce(mouseMove, 1000) //不立即执行。使用场景:input延迟验证。
app.onmousemove = debounce(mouseMove, 1000, true) //立即执行。使用场景:下拉刷新。
</script>
</body>
</html>
上述代码解析:
防抖函数通过闭包特性将timer一直留存在内存中,每次调用debounce都会先清除之前的定时器,如果不清除,则之前的定时器会对之后的定时器产生影响。由于之前定时器可能是执行timer = null
所以会导致timer结果不是预期结果。
之后判断是否立即执行回调函数
如果是立即执行回调函数,则需要判断是否timer是null,如果timer是null则执行回调函数;如果timer不为null,则定时器重新计时。
如果不是立即执行回调,则比较简单,只需每次执行防抖函数时重新设置定时器就行。
是null,如果timer是null则执行回调函数;如果timer不为null,则定时器重新计时。
如果不是立即执行回调,则比较简单,只需每次执行防抖函数时重新设置定时器就行。
延迟执行效果:
立即执行效果: