写在前面的话
我们前端在开发过程经常用到 scroll 、resize等高频的事件,特别在监测浏览器变化以及在下拉加载大数据的过程中,当这些高频的事件发生时,这些高频的事件触发的频次非常高,也非常大的多,间隔的时间也很短。如果这些高频的事件中要及到大量的位置计算、DOM 操作、UI重绘等等业务,但是偏偏业务计算和操作无法在下一个 高频的事件触发之前前完成,往往会造成严重的结果,这个节结果叫浏览器掉帧。持续触发高频的事件往往会导致掉帧扩大、浏览器 CPU 使用率飙升、用户体验受到影响、浏览器崩溃。如果这个高频的时间在涉及到后端接口的交互中,而前端依赖于这个高频事件如resize,scroll等,来触发和发送相应的Http请求,在这个整个实现的过程中,我们如果不做防抖处理,那么在这个高频的事件触发的一瞬间,就会有发出无数个请求给后端,而后端在处理这些请求增加了很多没有必要的压力,更有甚者造成服务器宕机。
概念
什么是函数节流
比如我举个生活中常见的列子,有个人去拿一个空桶取打水,但是这个比较珍惜时间,不想把时间浪费在打水的过程中,于是他就把水桶放在水龙头下面,但是要把水龙头全部打开 ,打满水需要5分钟,可是中途办事情需要10分钟,可是如果全开,再去办事情,回来之后,会有很多水浪费掉,于是,就把水龙头打开一半,然后去办事情,正好回来,水桶刚好满,也减少浪费,也充分利用时间和资源。而从全部打开到打开一半,这个就是节流,节流用大白话说就是让它流动的慢些。
什么是函数去抖
最早接触这个词应该是在高中物理里面学到的,有时候开关在在真正闭合之前可能会发生一些抖动现象,如果抖动的明显的话,对应的小灯泡可能会闪烁,把灯泡闪坏了不重要,万一把眼睛再给闪坏了可就麻烦了,这个时候就有去抖电路的出现。而在我们的页面里会有这样的一个业务,页面中有一个输入框,我们根据输入框里输入内容同时可能会去掉用http接口去查询对应的数据,如果这个用户输入的同时,频繁的触发input事件,然后频繁的向后台发送请求,那么直到用户输入完成时,之前的请求都应该是多余的,假设网络慢一点,后台返回的数据比较慢,那么显示的数据可能会出现频繁的变换,直到最后的一个请求返回。
节流和去抖区别
防抖动是将多次执行变为最后一次执行,节流是将多次执行变成每隔一段时间执行。
适用场景
- window对象的resize事件
- window对象的scroll事件
- 拖拽时的mousemove事件
- 鼠标的滚动,按下,弹出等事件
- 文字输入、自动完成的keyup事件
- …等等
引用节流和不引用对比
不使用节流
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Page Title</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>
</head>
<body>
<div style="margin:100px;height:200px;width:200px;background:red;"></div>
<script>
var i = 0
window.addEventListener('resize', function() {
console.log(i)
i ++
})
</script>
</body>
</html>
移动一下触发的控制台打印情况
使用节流
节流的写法
function throttle (fn, delay = 14, mustRunDelay = 14) {
let timer = null
let timestamp
return function () {
let context = this
let args = arguments
let now = +new Date()
clearTimeout(timer) // 清除定时器
if (!timestamp) {
timestamp = now
}
if (now - timestamp >= mustRunDelay) {
fn.apply(context, args)
timestamp = now
} else {
timer = setTimeout(function () {
fn.apply(context, args)
}, delay)
}
}
}
demo
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Page Title</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>
</head>
<body>
<div style="margin:100px;height:200px;width:200px;background:red;"></div>
<script>
function throttle (fn, delay = 14, mustRunDelay = 14) {
let timer = null
let timestamp
return function () {
let context = this
let args = arguments
let now = +new Date()
clearTimeout(timer) // 清除定时器
if (!timestamp) {
timestamp = now
}
if (now - timestamp >= mustRunDelay) {
fn.apply(context, args)
timestamp = now
} else {
timer = setTimeout(function () {
fn.apply(context, args)
}, delay)
}
}
}
var i = 0
window.addEventListener('resize', throttle(function() {
console.log(i)
i ++
}, 30, 30))
</script>
</body>
</html>
移动好几下触发的控制台打印情况
总结是不是效果很明显。结果显而易见,在大数据情况下或者其他业务无疑是最好的实现。赶紧把这个功能收藏,或者加入你的项目中吧。
引用函数去抖动和不引用对比
不使用函数去抖动
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Page Title</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>
</head>
<body style="height: 5000px;">
<div style="margin:100px;height:200px;width:200px;background:red;"></div>
<script>
var i = 0
function handleAppend() {
console.log(i);
i ++
}
window.addEventListener('scroll', handleAppend)
</script>
</body>
</html>
控制台打印情况
使用函数去抖动
函数去抖动的写法
function debounce(fn, wait) {
var timeout = null;
return function() {
if(timeout !== null)
clearTimeout(timeout);
timeout = setTimeout(fn, wait);
}
}
demo
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Page Title</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>
</head>
<body style="height: 5000px;">
<div style="margin:100px;height:200px;width:200px;background:red;"></div>
<script>
function debounce(fn, wait) {
var timeout = null;
return function() {
if(timeout !== null)
clearTimeout(timeout);
timeout = setTimeout(fn, wait);
}
}
var i = 0
function handleAppend() {
console.log(i);
i ++
}
window.addEventListener('scroll', debounce(handleAppend, 1000))
</script>
</body>
</html>
控制台打印情况