性能优化之函数防抖和函数节流
函数防抖
- 定义:触发事件后,在n秒内函数只能执行一次,如果触发事件后在n秒内又触发了事件,则会重新计算函数延长执行的时间。
- 个人总结:使重复执行的函数在短时间内只执行最后一次。
- 实现:
- 准备一个定时器,把要做的事情放在定时器里;
- 判断是否存在定时器,没有就开始计时,有就重新计时;
- 一直重新计时,直到没有操作的时候就只有一个定时器,所以只执行一次;
- 举个栗子
- 没有使用防抖的时候
1
2
3
4
5
6
7
8
9
10
11
12
13
<!-- 给div高度撑出滚动条 -->
<div style="height: 8000px;width: 100px;"></div>
<script>
const showLocaltion = () => {
// 获取滚动条位置(兼容写法)
var scrollTop = document.documentElement.scrollTop || window.pageYOffset || document.body.scrollTop
// 没有防抖的时候按方向键也会执行7、8次
console.log('滚动条位置:', scrollTop)
}
// 事件监听
window.addEventListener('scroll',showLocaltion)
</script> - 使用防抖
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24<!-- 滚动条不停滚动,只打印一次滚动条位置-->
<div style="height: 8000px;width: 100px;"></div>
<script>
const showLocaltion = () => {
// 获取滚动条位置(兼容写法)
var scrollTop = document.documentElement.scrollTop || window.pageYOffset || document.body.scrollTop
// 按方向键也会执行7、8次
console.log('滚动条位置:', scrollTop)
}
// 事件监听
// 存储定时器ID
let timer = null
window.addEventListener('scroll', () => {
if (timer) {
// 有定时器就先清除再重新开始计时
clearTimeout(timer)
timer = setTimeout(showLocaltion, 1000)
} else {
// 没有定时器就直接开始计时
timer = setTimeout(showLocaltion, 1000)
}
})
</script> - 因为得有个外层变量存储定时器ID,所以也可以使用闭包写法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25<div style="height: 8000px;width: 200px;"></div>
<script>
const showLocaltion = () => {
// 获取滚动条位置(兼容写法)
var scrollTop = document.documentElement.scrollTop || window.pageYOffset || document.body.scrollTop
// 按方向键也会执行7、8次
console.log('滚动条位置:', scrollTop)
}
function debounce(fn, delay) {
let timer = null
return function () {
if (timer) {
clearTimeout(timer)
timer = setTimeout(fn, delay)
} else {
timer = setTimeout(fn, delay)
}
}
}
window.onscroll = debounce(showLocaltion, 1000)
</script>
- 没有使用防抖的时候
函数节流
定义:如果短时间内大量触发同一事件,那么在事件处理函数执行一次之后,该函数在指定的时间期限内不再工作,直至过了这段时间才重新生效。
个人总结:使重复执行的函数每隔一段时间执行一次。
实现:
- 准备一个开关,事件处理程序根据开关来决定是否执行
- 开始执行函数之前关闭开关
- 每隔一段时间之后就打开开关
举个栗子
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27<!-- 函数节流 -->
<!-- 滚动条不停滚动,每隔一秒打印一次位置 -->
<!-- 准备一个开关,事件处理程序根据开关来决定是否执行 -->
<!-- 撑出滚动条 -->
<div style="height: 8000px;width: 100px;"></div>
<script>
const showLocaltion = () => {
var scrollTop = document.documentElement.scrollTop || window.pageYOffset || document.body.scrollTop
console.log('滚动条位置:', scrollTop)
// 隔段事件后打开开关,下次才能进入该分支
isGo = true
}
// 事件监听
// 开关
var isGo = true
window.addEventListener('scroll', () => {
if (isGo) {
// 关掉开关
isGo = false
setTimeout(showLocaltion, 1000)
} else {
return
}
})
</script>- 闭包写法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29<div style="height: 8000px;width: 200px;"></div>
<script>
const showLocaltion = () => {
var scrollTop = document.documentElement.scrollTop || window.pageYOffset || document.body.scrollTop
console.log('滚动条位置:', scrollTop)
// 隔段事件后打开开关,下次才能进入该分支
isGo = true
}
function throttle(fn, delay) {
let isGo = true
return function () {
if (isGo) {
// 关掉开关
isGo = false
setTimeout(()=>{
fn()
// 隔段事件后打开开关,下次才能进入该分支
isGo = true
}, delay)
}else{
return
}
}
}
window.onscroll = throttle(showLocaltion, 1000)
</script>
- 闭包写法
应用场景
节流(throttle)
- DOM元素的拖拽功能实现(mousemove)
- 搜索联想(keyup)
- 计算鼠标移动的距离(mousemove)
- Canvas 模拟画板功能(mousemove)
- 射击游戏的 mousedown/keydown 事件(单位时间只能发射一颗子弹)
- 监听滚动事件判断是否到页面底部自动加载更多:给 scroll 加了 debounce 后,只有用户停止滚动后,才会判断是否到了页面底部;如果是 throttle 的话,只要页面滚动就会间隔一段时间判断一次
防抖(debounce)
- scroll事件滚动触发事件
- 搜索框输入查询,如果用户一直在输入中,没有必要不停地调用去请求服务端接口,等用户停止输入的时候,再调用,设置一个合适的时间间隔,有效减轻服务端压力。
- 表单验证
- 按钮提交事件。
- 浏览器窗口缩放,resize事件(如窗口停止改变大小之后重新计算布局)等。
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Kenis!
评论