在项目中有一个需求,侧边栏回到顶部的组件由原来的 页面滚动到一定距离后持续显示 改为 当页面滚动时不显示,页面停止滚动时显示。
由于 window 只提供了监听页面滚动的方法:window.addEventListener('scroll',el,false),所以监听页面滚动完成的事件就需要手动完成,以下是我的代码实现:
// 监听页面滚动完成事件
this.handleScrollEnd=()=>{
let scrollTop = this.box.scrollTop || document.body.clientHeight;
if(scrollTop === this.currentTop){
clearTimeout(this.timer);
}
this.showTop = true;
if(this.box.scrollTop == 0){
this.showTop = false;
}
};
// 监听页面滚动事件
this.handler=()=>{
this.timer&&clearTimeout(this.timer);
this.timer = setTimeout(()=>{
this.handleScrollEnd();
},100);
}
主要就是通过定时器判断上一个时间周期的 scrollTop 是否与下一个时间周期的相等,如果相等,也就是说此时页面是静止的,侧边栏也就显示。
虽然看起来没有什么大问题,但是出于性能的考虑,当页面滚动时就会一直监听,也就是说这里如果做一个防抖或者节流,会大大提高页面性能。
好了,前提讲完了,接下来开始正题:
先搬出来一个小demo:
<div onmousemove="debounce(conSole,1000)">输出12345</button>
<script>
function conSole(){
console.log('12345')
}
</script>
所有人都能看懂,当鼠标滑动这个div时,会输出‘12345’
但是这个输出真的是,高频率,只要滑动就会不停地输出,所以 防抖函数就来啦!
防抖
简单的思路就是,在第一次滑动时,不立即触发这个函数,设置一个定时器,如果规定的延迟时间内,没有再次触发滑动事件,就执行这个滑动事件,如果再次触发了,就重新计时,这样操作的效果就是,短时间内多次触发这个事件,就只执行一次。以下是实现的代码:
<div onmousemove="debounce(conSole,1000)">输出12345</div>
<script>
function debounce(fn,delay){
let timer = null;
return function(){
if(timer){
clearTimeout(timer);
timer = setTimeout(fn,delay)
}else{
timer = setTimeout(fn,delay)
}
}()
}
function conSole(){
console.log('12345')
}
</script>
下面的GIF是防抖后的效果:
可以很明显的看出,并没有持续输出,而是有延时的,停止1000ms后,才会输出。
所以总结一下防抖的概念:当持续触发一个事件时,一段时间内没有再触发事件,才会执行一次,如果在设定的时间内又触发了事件,则重新开始延时。
防抖是触发事件之后的一段时间后执行,而节流是持续触发事件的一段时间内只执行一次事件。
节流
还是先来个小demo:
<div onclick="debounce(conSole,1000)">输出12345</div>
<script>
function throttle(fn,delay){
let prev = new Date().getTime();
return function(){
let now = new Date().getTime();
if( now - prev >= delay){
fn.apply(this,arguments);
prev = new Date().getTime();
}
}()
}
function conSole(){
console.log('12345')
}
</script>
效果图如下:
这个GIF期间我是一直点击的,只有当前时间戳-上一个时间戳的值小于延迟时间才可以触发输出。
上述的节流方法是获取时间戳进行比较的方法,下面介绍另一种方法,定时器:
还是老样子 上demo
function throttle(fn,delay){
let timer = null;
return function(){
if(!timer){
timer = setTimeout(function(){
fn.apply(this,arguments);
timer = null;
},delay)
}
}()
}
效果图如下:
从效果图可以看出,当我一直滑动div时,并不会立即输出,而是等时间延迟后才会输出,(由于在区域内就是滑动,所以一直触发滑动)。
总结
防抖就是将多次操作合并为一次,节流是一段时间内只触发一次;总的来说这两种方法都提高了性能,在多次触发事件、请求数据时,都可以使用。