zoukankan      html  css  js  c++  java
  • 节流和防抖

    项目首页做了滚动加载,给鼠标绑定事件之后,用户的每一次滚动都会触发事件,这个会造成性能的浪费,节流和防抖可以帮助优化。
    其实不止 scroll 事件,resize 事件、鼠标事件(比如 mousemove、mouseover 等)、键盘事件(keyup、keydown 等)都存在被频繁触发的风险。

    “节流”与“防抖”的本质

    这两个东西都以闭包的形式存在。

    它们通过对事件对应的回调函数进行包裹、以自由变量的形式缓存时间信息,最后用 setTimeout 来控制事件的触发频率。

    Throttle: 第一个人说了算

    throttle 的中心思想在于:在某段时间内,不管你触发了多少次回调,我都只认第一次,并在计时结束时给予响应。

    Debounce: 最后一个人说了算

    防抖的中心思想在于:我会等你到底。在某段时间内,不管你触发了多少次回调,我都只认最后一次。

    优化

    debounce 的问题在于它“太有耐心了”。试想,如果用户的操作十分频繁——他每次都不等 debounce 设置的 delay 时间结束就进行下一次操作,于是每次 debounce 都为该用户重新生成定时器,回调函数被延迟了不计其数次。频繁的延迟会导致用户迟迟得不到响应,用户同样会产生“这个页面卡死了”的观感。

    为了避免弄巧成拙,我们需要借力 throttle 的思想,打造一个“有底线”的 debounce——等你可以,但我有我的原则:delay 时间内,我可以为你重新生成定时器;但只要delay的时间到了,我必须要给用户一个响应。

    下面是对应的三种实现:


    let last = 0, timer = null; // 把上次触发事件和定时器存在全局

    /**
    * 防抖
    * @param fn
    * @param delay
    * @returns {Function}
    */
    debounce=(fn, delay)=>{
    // let timer = null;
    // 将debounce处理结果当作函数返回
    return function () {
    // 保留调用时的this上下文
    let context = this
    // 保留调用时传入的参数
    let args = arguments

    // 每次事件被触发时,都去清除之前的旧定时器
    if(timer) {
    clearTimeout(timer)
    }
    // 设立新定时器
    timer = setTimeout(function () {
    fn.apply(context, args)
    }, delay)
    }
    }
    /**
    * 节流
    * @param fn
    * @param interval
    * @returns {Function}
    */
    throttle=(fn, interval)=>{
    // 将throttle处理结果当作函数返回
    return function () {
    // 保留调用时的this上下文
    let context = this
    // 保留调用时传入的参数
    let args = arguments
    // 记录本次触发回调的时间
    let now = +new Date()

    // 判断上次触发的时间和本次触发的时间差是否小于时间间隔的阈值
    if (now - last >= interval) {
    // 如果时间间隔大于我们设定的时间间隔阈值,则执行回调
    last = now;
    fn.apply(context, args);
    }
    }
    }
    /**
    * 节流防抖结合
    * @param fn
    * @param delay
    * 用 Throttle 来优化 Debounce
    */
    throttle=(fn, delay)=>{
    // 将throttle处理结果当作函数返回
    return function(){
    // 保留调用时的this上下文
    let context = this;
    // 保留调用时传入的参数
    let args = arguments;
    // 记录本次触发回调的时间
    let now = +new Date()

    // 判断上次触发的时间和本次触发的时间差是否小于时间间隔的阈值
    if(now - last < delay){
    console.log("不触发")
    clearTimeout(timer)
    timer = setTimeout(function(){
    last = now;
    fn.apply(context, args);
    }, delay)
    }else{
    console.log("触发")
    // 如果时间间隔超出了我们设定的时间间隔阈值,那就不等了,无论如何要反馈给用户一次响应
    last = now
    fn.apply(context, args)
    }
    }
    }

  • 相关阅读:
    delphi 获取图片某一像素的颜色值
    delphi常用快捷键(我自己经常使用的)
    115.css的initial、inherit、unset
    菜鸡前端的配色记录-echart比较好看的配色
    114.关于前端的vertical-align详解
    Problem
    Oracle从入门到入门的学习历程
    使用navicat连接oracleXE
    使用ElementUI遭遇问题记录
    SonarQube遇见的问题
  • 原文地址:https://www.cnblogs.com/gwf93/p/10147918.html
Copyright © 2011-2022 走看看