前提
在进行窗口的resize,scroll,输入框内容校验的过程中,如果调用事件的频率非常高,会加重浏览器的负担,用户体验非常糟糕,可以采用防抖和节流的方法减少调用频率,同时又不影响实际使用效果。
监听浏览器滚动事件,返回当前滚条与顶部的距离
function showTop () {var scrollTop = document.body.scrollTop || document.documentElement.scrollTop;console.log('滚动条位置:' + scrollTop);}window.onscroll = showTop
函数防抖
函数防抖(debounce):当持续触发事件时,一定时间段内没有再触发事件,事件处理函数才会执行一次,如果设定的时间到来之前,又一次触发了事件,则当前计时取消,重新开始计时
举例:
在第一次触发事件时,不立即执行函数,而是给出一个期限值比如200ms,然后:
- 如果在200ms内没有再次触发滚动事件,那么就执行函数
- 如果在200ms内再次触发滚动事件,那么当前的计时取消,重新开始计时
function debounce(fn,delay){let timer = null //借助闭包return function() {if(timer){//重新计时clearTimeout(timer)}timer = setTimeout(fn,delay) // 简化写法}}// 然后是旧代码function showTop () {var scrollTop = document.body.scrollTop || document.documentElement.scrollTop;console.log('滚动条位置:' + scrollTop);}window.onscroll = debounce(showTop,1000)
什么是闭包
闭包是指有权访问另外一个函数作用域中的变量的函数。可以理解为(能够读取另一个函数作用域的变量的函数)
对于短时间内连续触发的事件(如滚动事件),防抖的含义就是在某个时间期限(如1000毫秒)内,事件处理函数只执行一次。function outer() {var a = '变量1'var inner = function () {console.info(a)}return inner // inner 就是一个闭包函数,因为他能够访问到outer函数的作用域}
函数节流
当持续触发事件时,保证一定时间段内只调用一次事件处理函数。比如水龙头开小一点,控制水流小一点,即在函数执行一次之后,该函数在指定的时间期限内不再工作,直至过了这段时间才重新生效。
主要有两种实现方法:时间戳和定时器时间戳
var throttle = function(func, delay) {var prev = Date.now();return function() {var context = this;var args = arguments;var now = Date.now();if (now - prev >= delay) {func.apply(context, args);prev = Date.now();}}}function handle() {console.log(Math.random());}window.addEventListener('scroll', throttle(handle, 1000));
定时器
// 节流throttle代码(定时器):var throttle = function(func, delay) {var timer = null;return function() {var context = this;var args = arguments;if (!timer) {timer = setTimeout(function() {func.apply(context, args);timer = null;}, delay);}}}function handle() {console.log(Math.random());}window.addEventListener('scroll', throttle(handle, 1000));
举例:
```javascript function throttle(fn,delay){ let valid = true return function() {
} } / 请注意,节流函数并不止上面这种实现方案, 例如可以完全不借助setTimeout,可以把状态位换成时间戳,然后利用时间戳差值是否大于指定间隔时间来做判定。 也可以直接将setTimeout的返回的标记当做判断条件-判断当前定时器是否存在,如果存在表示还在冷却,并且在执行fn之后消除定时器表示激活,原理都一样 /if(!valid){//休息时间 暂不接客return false}// 工作时间,执行函数并且在间隔期内把状态位设为无效valid = falsesetTimeout(() => {fn()valid = true;}, delay)
// 以下照旧 function showTop () { var scrollTop = document.body.scrollTop || document.documentElement.scrollTop; console.log(‘滚动条位置:’ + scrollTop); } window.onscroll = throttle(showTop,1000) 举例:
如果一直拖着滚动条进行滚动,那么会以1s的时间间隔,持续输出当前位置和顶部的距离
<a name="DRrw2"></a>
### 代码:
```javascript
<template>
<div>
<template v-for="(item) in 300">
<div style="font-size:30px">测试{{item}}</div>
</template>
</div>
</template>
<script>
export default {
name: "test",
data() {
return {}
},
methods: {
showTop() {
var scrollTop = document.body.scrollTop || document.documentElement.scrollTop;
console.log('滚动条位置:' + scrollTop);
},
/**
* @name 防抖
* @param fn
* @param delay
* @returns {Function}
*/
debounce(fn, delay) {
let timer = null; //借助闭包
return function () {
if (timer) {
return false
}
timer=setTimeout(() => {
fn();
clearTimeout(timer);
timer = null;
}, delay) // 简化写法
}
},
/**
* @name 节流
* @param fn
* @param delay
* @returns {Function}
*/
throttle(fn, delay) {
let valid = true;
return function () {
if (!valid) {
//休息时间 暂不接客
return false
}
// 工作时间,执行函数并且在间隔期内把状态位设为无效
valid = false;
setTimeout(() => {
fn();
valid = true;
}, delay)
}
}
},
mounted() {
// window.addEventListener('scroll', this.showTop)
window.addEventListener('scroll', this.debounce(this.showTop, 1000))
// window.addEventListener('scroll', this.throttle(this.showTop, 1000))
}
}
</script>
<style scoped>
</style>
