网页性能优化03-函数防抖

Posted 坤小

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了网页性能优化03-函数防抖相关的知识,希望对你有一定的参考价值。

1.1-函数防抖

1.函数防抖介绍

  • 1.什么是函数防抖? (debounce)

    • 网上主流解释:函数防抖,就是指触发事件后在 n 秒内函数只能执行一次,如果在 n 秒内又触发了事件,则会重新计算函数执行时间。
      • 参考博客:https://www.jianshu.com/p/f9f6b637fd6c
      • 参考博客:https://segmentfault.com/a/1190000018445196
    • 笔者解释
      • 先理解什么是抖动?:例如用户鼠标轻微晃动,快速划过某一个元素(用户本身不想触发,只是鼠标误触发,常见于鼠标事件 移入/移出/移动 )。 例如输入框手机和邮箱验证,用户在不停的输入,还没有输完的时候其实是不需要验证的,应该等用户输入完毕后再验证。
      • 防抖 :如果用户鼠标轻微晃动,在某一个元素上停留时间很短,则认为是用户误触发,则不执行本次事件处理函数
        • 一句话总结:用户连续多次触发某个事件,则只执行最后一次
      • 由于函数防抖 属于 前端中的 网页性能优化技术,因此初学者刚开始学习会有一些吃力,并且很多网站都没有做防抖处理(性能优化)
        • 没有函数防抖的真实案例:http://www.elong.com/?semid=ppzqbaidu
          • 这是一个旅游类网站,上面酒店类型选择没有做防抖处理,用户体验很差
        • 有函数防抖的真实案例:https://xyq.163.com/
          • 这是网易梦幻西游官网,在网页底部选择职业的手风琴位置有做防抖处理,用户体验较好
  • 没有做函数防抖处理的用户体验如下

    • 假设当前鼠标在第一张,此时用户想看第五张。正常情况下,鼠标会依次触发 第二、第三、第四张的移入事件,但这不是用户真正想要触发的(误触发)

  • 有做函数防抖处理的用户体验如下
    • 用户从第一张 滑动到第五张,由于鼠标在 第二、第三、第四张停留时间很短(假设小于0.5秒),所以判定为用户误触发,则不触发对应的事件处理函数

2.函数防抖解决思路

  • 使用定时器:保证用户多次触发事件时,以最后一次触发为准

  • 1.每一次移入元素时 : 不立即触发该事件处理函数,而是开启定时器,间隔0.5s(防抖间隔)之后再执行事件处理函数。

    • 此时并没有彻底解决函数防抖,因为用户多次触发事件时,每一个定时器都会在0.5s之后,依次执行
  • 2.每一次移入元素时 : 先清除上一次的定时器

    • 保证用户多次触发事件时,以最后一次触发为准
  • 注意点:定时器中的this默认为window,需要使用上下文模式bind()动态修改为当前事件源

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>动画-案例《手风琴》</title>
    <style>
        * 
            margin: 0;
            padding: 0;
        

        ul 
            list-style: none;
            width: 2400px;
        

        #box 
            width: 1200px;
            height: 400px;
            border: 2px solid red;
            margin: 100px auto;
            overflow: hidden;
        

        #box li 
            width: 240px;
            height: 400px;
            float: left;
        
    </style>
</head>

<body>
    <div id="box">
        <ul>
            <li><img src="./images/collapse/1.jpg" alt=""></li>
            <li><img src="./images/collapse/2.jpg" alt=""></li>
            <li><img src="./images/collapse/3.jpg" alt=""></li>
            <li><img src="./images/collapse/4.jpg" alt=""></li>
            <li><img src="./images/collapse/5.jpg" alt=""></li>
        </ul>
    </div>

    <script src="./animation.js"></script>
    <script>

        /*需求
        (1):给每一个li设置鼠标移入事件:当前li的宽度变成800,其他兄弟li宽度变成100
        (2):鼠标移出大盒子,所有的li的宽度都变成默认的240 
         */

        //1.获取元素
        var liList = document.querySelectorAll('#box li');//li元素列表
        var box = document.querySelector('#box');//li元素列表

        /* 一:声明全局变量存储定时器ID */
        var timeID = null;

        //2.注册事件
        //2.1 鼠标移入事件:当前li的宽度变成800,其他兄弟li宽度变成100
        for (var i = 0; i < liList.length; i++) 
            liList[i].onmouseover = function () 

                //二:先清除以前的定时器,以本次为准
                clearTimeout(timeID);
                //事件处理函数 : this 指向事件源

                /* 三:当事件被触发时,用户可能是误操作,所以开启定时器等一会儿再执行
                    注意点: 定时器中的this默认一定是window,需要动态修改为事件源
                 */
                timeID = setTimeout(function () 
                    console.log(1111);
                    // 定时器中的this : 默认指向window
                    //3.排他思想修改样式
                    for (var j = 0; j < liList.length; j++) 
                        if (liList[j] == this) //是自己
                            animationSlow(liList[j],  width: 800 );
                         else 
                            animationSlow(liList[j],  width: 100 );
                        
                    ;
                .bind(this), 500);

            ;
        ;



        //2.2 鼠标移出大盒子,所有的li的宽度都变成默认的240 
        box.onmouseout = function (e) 
            /* 由于受到事件冒泡影响,当鼠标滑出某一个li元素也会触发父元素的移出事件
            解决方案:判断事件触发源是不是box,是的话才修改li元素宽度为240
             */
            if (e.target == box) 
                for (var i = 0; i < liList.length; i++) 
                    animationSlow(liList[i],  width: 240 );
                ;
            ;
        ;


    </script>

</body>

</html>

3.万能防抖函数的封装

  • 为什么要封装万能的防抖函数
    • 在上一个小节中,我们的重点是介绍函数防抖的思路。但是在实际开发中,每一个防抖函数的事件处理都是不一样的,他们可能是鼠标移入、鼠标移出、鼠标移动。 每一个案例需要的防抖间隔也不同
/**
* @description: 万能防抖函数
* @param type fn : 事件处理函数
* @param type timeout : 防抖时间间隔
* @return: 
*/
function debounce(fn, timeout) 
    /*核心技术介绍 
    1. 函数防抖需要使用定时器,但是定时器id不能是局部的  (局部变量函数执行完毕会被回收)
    2. 定时器id如果使用全局变量存储,则会造成全局变量污染
    3. 解决方案 :
         (1)使用闭包延长局部变量声明周期,但是语法过于繁琐
         (2)利用函数本身也是对象,使用函数本身的静态成员来存储定时器id
    */

     //1.先清除上一次触发事件的定时器
     clearTimeout(debounce.timeID);
     //2.以最后一次触发为准
     debounce.timeID = setTimeout(fn, 500);
;
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>动画-案例《手风琴》</title>
    <style>
        * 
            margin: 0;
            padding: 0;
        

        ul 
            list-style: none;
            width: 2400px;
        

        #box 
            width: 1200px;
            height: 400px;
            border: 2px solid red;
            margin: 100px auto;
            overflow: hidden;
        

        #box li 
            width: 240px;
            height: 400px;
            float: left;
        
    </style>
</head>

<body>
    <div id="box">
        <ul>
            <li><img src="./images/collapse/1.jpg" alt=""></li>
            <li><img src="./images/collapse/2.jpg" alt=""></li>
            <li><img src="./images/collapse/3.jpg" alt=""></li>
            <li><img src="./images/collapse/4.jpg" alt=""></li>
            <li><img src="./images/collapse/5.jpg" alt=""></li>
        </ul>
    </div>

    <script src="./animation.js"></script>
    <script>

        /*需求
        (1):给每一个li设置鼠标移入事件:当前li的宽度变成800,其他兄弟li宽度变成100
        (2):鼠标移出大盒子,所有的li的宽度都变成默认的240 
         */

        //1.获取元素
        var liList = document.querySelectorAll('#box li');//li元素列表
        var box = document.querySelector('#box');//li元素列表

        //2.注册事件
        //2.1 鼠标移入事件:当前li的宽度变成800,其他兄弟li宽度变成100
        for (var i = 0; i < liList.length; i++) 
            liList[i].onmouseover = function () 
                debounce(function () 
                    for (var j = 0; j < liList.length; j++) 
                        if (liList[j] == this) //是自己
                            animationSlow(liList[j],  width: 800 );
                         else 
                            animationSlow(liList[j],  width: 100 );
                        
                    ;
                .bind(this), 500);
            ;
        ;

        /**
        * @description: 万能防抖函数
        * @param type fn : 事件处理函数
        * @param type timeout : 防抖时间间隔
        * @return: 
        */
        function debounce(fn, timeout) 
            /*核心技术介绍 
            1. 函数防抖需要使用定时器,但是定时器id不能是局部的  (局部变量函数执行完毕会被回收)
            2. 定时器id如果使用全局变量存储,则会造成全局变量污染
            3. 解决方案 :
                 (1)使用闭包延长局部变量声明周期,但是语法过于繁琐
                 (2)利用函数本身也是对象,使用函数本身的静态成员来存储定时器id
            */

             //1.先清除上一次触发事件的定时器
             clearTimeout(debounce.timeID);
             //2.以最后一次触发为准
             debounce.timeID = setTimeout(fn, 500);
        ;



        //2.2 鼠标移出大盒子,所有的li的宽度都变成默认的240 
        box.onmouseout = function (e) 
            /* 由于受到事件冒泡影响,当鼠标滑出某一个li元素也会触发父元素的移出事件
            解决方案:判断事件触发源是不是box,是的话才修改li元素宽度为240
             */
            if (e.target == box) 
                for (var i = 0; i < liList.length; i++) 
                    animationSlow(liList[i],  width: 240 );
                ;
            ;
        ;


    </script>

</body>

</html>

以上是关于网页性能优化03-函数防抖的主要内容,如果未能解决你的问题,请参考以下文章

JavaScript性能优化8——防抖与节流

JavaScript性能优化8——防抖与节流

性能优化之函数防抖动

函数防抖和节流*(性能优化不错的选择)

js前端性能优化之函数节流和函数防抖

性能优化——防抖和节流