项目踩坑1-节流与防抖

项目:基于vue的专业课程系统

遇到问题: 下拉显示更多的电影,实现一个鼠标拖拽Y轴到底部显示一个返回首页的游标的组件时资源加载慢页面卡顿

节流与防抖是什么

是优化高频率执行代码的一种手段:如window对象频繁的onresize onscroll keypress mousemove 等事件在出发时, 会不断地调用绑定在事件上的回调函数,浪费资源,利用节流与防抖减少调用

定义

 节流(throttle ): n秒内只执行一次,若在n秒内重复触发,只有一次生效

  防抖(debounce):n秒后再执行该事件,若在n秒内被重复触发, 则重新记时

  假设电梯有两种运行策略 debounce 和 throttle,超时设定为15秒,不考虑容量限制

  电梯第一个人进来后,15秒后准时运送一次,这是节流

  电梯第一个人进来后,等待15秒。如果过程中又有人进来,15秒等待重新计时,直到 15秒后开始运送,这是防抖

节流

时间戳,事件会立即执行,停止触发后没有办法再次执行

function throttle(func, delay = 500) {
    let oldtime = Date.now()
    return function (...args) {
        let newtime = Date.now()
        if(newtime- oldtime >= delay) {
            func.apply(null, args)
            oldtime = Date.now()
        }
    }
}

func.apply(thisArg, [argsArray])

参数

thisArg
必选的。在 func 函数运行时使用的 this 值。请注意,this可能不是该方法看到的实际值:如果这个函数处于非严格模式下,则指定为 null 或 undefined 时会自动替换为指向全局对象,原始值会被包装。
argsArray
可选的。一个数组或者类数组对象,其中的数组元素将作为单独的参数传给 func 函数。如果该参数的值为 null 或  undefined,则表示不需要传入任何参数。从ECMAScript 5 开始可以使用类数组对象。

返回值

调用有指定this值和参数的函数的结果。

 

 定时器写法

function throttle(func, delay = 500) {
    let timer = null;
    return function (...args) {
        if(!timer) {
            timer = setTimeout(() => {
                func.apply(this, args)
                timer = null
            }, delay)
        }
    }
}
// dealy毫秒后第一次执行, 第二次事件停止触发后依然会再一次执行

精确写法

function throttle(func, delay) {
    let timer = null;
    let startTime = Date.now() //触发时时间
    return function () {
        let currentTime = Date.now(); //当前时间
        let remaining = delay -(currentTime - startTime);
        let context = this; 
        let args = arguments;
        clearTimeout(timer) 
        if(remaining <= 0) {
            func.apply(context, args)
            startTime = Date.now()
        }else{
            timer = setTimeout(func, remaining);
            // 时间戳版的函数触发是在时间段内开始的时候,而定时器版的函数触发是在时间段内结束的时候。
        }

    }
}

防抖

function debounce(func, wait) {
    let timeout;
    return function () {
        let context = this; //保存this指向
        let args = arguments; //拿到event对象

        clearTimeout(timeout) 
            
        timeout = setTimeout(function() {
                func.apply(context, args)
                
            }, wait)
        
    }
}

立即执行,加入第三参数用于判断

function debounce(func, wait, immediate) {
    let timeout;
    return function () {
        let context = this; //保存this指向
        let args = arguments; //拿到event对象

        if(timeout) clearTimeout(timeout)  //timeout 不为null
        if(immediate) {
            let callNow = !timeout; //第一次会立即执行,以后只有时间执行后才会再次触发
            timeout = setTimeout(function() {
                timeout = null 
            }, wait)
            if(callNow) {
                func.apply(context, args)
            }
        } 
        else {
            timeout = setTimeout(function () {
                func.apply(context, args)
            }, wait);
        }
        
        
    }
}

 

防抖在连续的事件,只需触发一次回调的场景有:

  • 搜索框搜索输入。只需用户最后一次输入完,再发送请求
  • 手机号、邮箱验证输入检测
  • 窗口大小resize。只需窗口调整完成后,计算窗口大小。防止重复渲染。

节流在间隔一段时间执行一次回调的场景有:

  • 滚动加载,加载更多或滚到底部监听
  • 搜索框,搜索联想功能
posted @ 2022-03-21 20:04  HaoyuSun  阅读(38)  评论(0)    收藏  举报