前提

在进行窗口的resize,scroll,输入框内容校验的过程中,如果调用事件的频率非常高,会加重浏览器的负担,用户体验非常糟糕,可以采用防抖和节流的方法减少调用频率,同时又不影响实际使用效果。

监听浏览器滚动事件,返回当前滚条与顶部的距离

  1. function showTop () {
  2. var scrollTop = document.body.scrollTop || document.documentElement.scrollTop;
  3.   console.log('滚动条位置:' + scrollTop);
  4. }
  5. window.onscroll = showTop

函数防抖

函数防抖(debounce):当持续触发事件时,一定时间段内没有再触发事件,事件处理函数才会执行一次,如果设定的时间到来之前,又一次触发了事件,则当前计时取消,重新开始计时

举例:

在第一次触发事件时,不立即执行函数,而是给出一个期限值比如200ms,然后:

  • 如果在200ms内没有再次触发滚动事件,那么就执行函数
  • 如果在200ms内再次触发滚动事件,那么当前的计时取消,重新开始计时
    1. function debounce(fn,delay){
    2. let timer = null //借助闭包
    3. return function() {
    4. if(timer){
    5. //重新计时
    6. clearTimeout(timer)
    7. }
    8. timer = setTimeout(fn,delay) // 简化写法
    9. }
    10. }
    11. // 然后是旧代码
    12. function showTop () {
    13. var scrollTop = document.body.scrollTop || document.documentElement.scrollTop;
    14.   console.log('滚动条位置:' + scrollTop);
    15. }
    16. window.onscroll = debounce(showTop,1000)

    什么是闭包

    闭包是指有权访问另外一个函数作用域中的变量的函数。可以理解为(能够读取另一个函数作用域的变量的函数)
    1. function outer() {
    2. var a = '变量1'
    3. var inner = function () {
    4. console.info(a)
    5. }
    6. return inner // inner 就是一个闭包函数,因为他能够访问到outer函数的作用域
    7. }
    对于短时间内连续触发的事件(如滚动事件),防抖的含义就是在某个时间期限(如1000毫秒)内,事件处理函数只执行一次。

    函数节流

    当持续触发事件时,保证一定时间段内只调用一次事件处理函数。比如水龙头开小一点,控制水流小一点,即在函数执行一次之后,该函数在指定的时间期限内不再工作,直至过了这段时间才重新生效。
    主要有两种实现方法:时间戳和定时器

    时间戳

    1. var throttle = function(func, delay) {
    2.   var prev = Date.now();
    3.   return function() {
    4.     var context = this;
    5.     var args = arguments;
    6.     var now = Date.now();
    7.     if (now - prev >= delay) {
    8.       func.apply(context, args);
    9.       prev = Date.now();
    10.     }
    11.   }
    12. }
    13. function handle() {
    14.   console.log(Math.random());
    15. }
    16. window.addEventListener('scroll', throttle(handle, 1000));

    定时器

    1. // 节流throttle代码(定时器):
    2. var throttle = function(func, delay) {
    3. var timer = null;
    4. return function() {
    5. var context = this;
    6. var args = arguments;
    7. if (!timer) {
    8. timer = setTimeout(function() {
    9. func.apply(context, args);
    10. timer = null;
    11. }, delay);
    12. }
    13. }
    14. }
    15. function handle() {
    16. console.log(Math.random());
    17. }
    18. window.addEventListener('scroll', throttle(handle, 1000));

    举例:

    ```javascript function throttle(fn,delay){ let valid = true return function() {
    1. if(!valid){
    2. //休息时间 暂不接客
    3. return false
    4. }
    5. // 工作时间,执行函数并且在间隔期内把状态位设为无效
    6. valid = false
    7. setTimeout(() => {
    8. fn()
    9. valid = true;
    10. }, delay)
    } } / 请注意,节流函数并不止上面这种实现方案, 例如可以完全不借助setTimeout,可以把状态位换成时间戳,然后利用时间戳差值是否大于指定间隔时间来做判定。 也可以直接将setTimeout的返回的标记当做判断条件-判断当前定时器是否存在,如果存在表示还在冷却,并且在执行fn之后消除定时器表示激活,原理都一样 /

// 以下照旧 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>