奥门金沙手机娱乐网址 27

关于js函数节流和去抖动【奥门金沙手机娱乐网址】

以前写过一篇文章:图片延迟加载之jQuery.lazyload,介绍了lazyload的用法和技巧,今天主要探讨在移动端应用的优化。

为啥要用图片懒加载

对页面加载速度影响最大的就是图片,一张普通的图片可以达到几M的大小,而代码也许就只有几十KB。当页面图片很多时,页面的加载速度缓慢,几S钟内页面没有加载完成,也许会失去很多的用户。
所以,对于图片过多的页面,为了加速页面加载速度,所以很多时候我们需要将页面内未出现在可视区域内的图片先不做加载,
等到滚动到可视区域后再去加载。这样子对于页面加载性能上会有很大的提升,也提高了用户体验。

实际上啊,今天早上本来想干点别的,但是在吃早餐的时候浏览了下掘金,然后看到了这篇博文<a
href=”;
就顺带研究了一波函数节流和去抖动问题。

可视区的计算

原理

将页面中的img标签src指向一张小图片或者src为空,然后定义data-src(这个属性可以自定义命名,我才用data-src)属性指向真实的图片。src指向一张默认的图片,否则当src为空时也会向服务器发送一次请求(指向默认的一张图那就只需请求一次)。可以指向loading的地址。

当载入页面时,先把可视区域内的img标签的data-src属性值负给src,然后监听滚动事件,把用户即将看到的图片加载。这样便实现了懒加载。

  • ps:图片要指定宽高

关于窗口各种宽度,给出网上找的一张好图

窗口各种宽度

如果仍不是很理解,看这两篇文章

scrollWidth,clientWidth,offsetWidth的区别

JS中关于clientWidth offsetWidth scrollWidth
等的含义

为什么要函数节流

以下场景往往由于事件频繁被触发,因而频繁执行DOM操作、资源加载等重行为,导致UI停顿甚至浏览器崩溃。

  1. window对象的resize、scroll事件
  2. 奥门金沙手机娱乐网址 ,拖拽时的mousemove事件
  3. 射击游戏中的mousedown、keydown事件
  4. 文字输入、自动完成的keyup事件例子以scroll事件进行解析<code>window.onscroll
    = function(){lazyload();//throttle(lazyload,window);};function
    lazyload(){console.log(“scroll执行了”+scrollnum);}</code>

    奥门金沙手机娱乐网址 1滚动一次

    我们的本意只是让鼠标滚动一次执行一次滚动函数,但是window的onscroll函数并不是等scroll结束之后才会调用,鼠标滚动或拖动滚动条,就会不停的触发scroll事件,如果处理的东西多,低版本的IE也会陷入假死状态。

通过对window绑定scroll事件,获取被隐藏在可视区域上方的像素数,
再计算img纵向偏移量。当被隐藏在可视区域上方的像素数大于img纵向偏移量+可视区高度时替换img的src。

图片懒加载的实现代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style>
        img {
            display: block;
            margin-bottom: 50px;
            width: 400px;
            height: 400px;
        }
    </style>
</head>
<body>
     <img src="" data-src="http://pic.58pic.com/58pic/17/18/97/01U58PIC4Xr_1024.jpg" alt="奥门金沙手机娱乐网址 2">
    <img src="" data-src="http://ww4.sinaimg.cn/large/006y8mN6gw1fa5obmqrmvj305k05k3yh.jpg" alt="奥门金沙手机娱乐网址 3">
    <img src="" data-src="http://ww1.sinaimg.cn/large/006y8mN6gw1fa7kaed2hpj30sg0l9q54.jpg" alt="奥门金沙手机娱乐网址 4">
    <img src="" data-src="http://cover.read.duokan.com/mfsv2/download/fdsc3/p01N203pHTU7/Wr5314kcLAtVCi.jpg!t" alt="奥门金沙手机娱乐网址 5">
    <img src="" data-src="http://77fkxu.com1.z0.glb.clouddn.com/20160308/1457402219_73571.jpg" alt="奥门金沙手机娱乐网址 6">
    <img src="" data-src="http://pic1.cxtuku.com/00/16/18/b3809a2ba0f3.jpg" alt="奥门金沙手机娱乐网址 7">
    <img src="" data-src="http://img.bitscn.com/upimg/allimg/c150708/14363B06253120-6060O.jpg" alt="奥门金沙手机娱乐网址 8">
    <img src="" data-src="http://cover.read.duokan.com/mfsv2/download/fdsc3/p015trgKM7vw/H0iyDPPneOVrA4.jpg!t" alt="奥门金沙手机娱乐网址 9">
    <img src="" data-src="http://ww1.sinaimg.cn/large/006y8mN6gw1fa7kaed2hpj30sg0l9q54.jpg" alt="奥门金沙手机娱乐网址 10">
    <img src="" data-src="http://imgsrc.baidu.com/baike/pic/item/2f9cbdcc5e0bcf5c00e9283b.jpg" alt="奥门金沙手机娱乐网址 11">
    <img src="" data-src="http://ww4.sinaimg.cn/large/006y8mN6gw1fa5obmqrmvj305k05k3yh.jpg" alt="奥门金沙手机娱乐网址 12">
<script>
    (function(){
    let num = document.getElementsByTagName('img').length;
    let img = document.getElementsByTagName("img");
    let n = 0; //存储图片加载到的位置,避免每次都从第一张图片开始遍历
    lazyload(); //页面载入完毕加载可是区域内的图片
     window.onscroll = lazyload;
    function lazyload() { //监听页面滚动事件
        let seeHeight = document.documentElement.clientHeight; //可见区域高度
        let scrollTop = document.documentElement.scrollTop || document.body.scrollTop; //滚动条距离顶部高度
        for (let i = n; i < num; i++) {
            // 图片未出现时距离顶部的距离大于滚动条距顶部的距离+可视区的高度
            if (img[i].offsetTop < seeHeight + scrollTop) {
                if (img[i].getAttribute("src") == "") {
                    img[i].src = img[i].getAttribute("data-src");
                }
                n = i + 1;
            }
        }
    }
    })()

</script>
</body>
</html>

demo:图片懒加载

debounce

抖动:如果用手指一直按住一个弹簧,它将不会弹起直到你松手为止。
也就是说当调用动作n毫秒后,才会执行该动作,若在这n毫秒内又调用此动作则将重新计算执行时间。这种比较适合window的resize事件,实际需求大多为停止改变大小n毫秒后执行后续处理;而其他事件大多的需求是以一定的频率执行后续处理。针对这两种需求就出现了和throttle两种解决办法。

  1. 去抖1<code>window.onscroll =
    function(){//lazyload();debounce(lazyload,window);};function
    debounce(method,context){clearTimeout(method.timeout);method.timeout
    = setTimeout(function(){method.call;},500);}function
    lazyload(){console.log(“scroll执行了”+scrollnum);}</code>效果如下,可以看出只执行了一次lazyload函数:

    奥门金沙手机娱乐网址 13节流之后的滚动一次的执行效果

    利用定时器,让函数执行延迟500毫秒,在500毫秒内如果有函数又被调用则删除上一次调用,这次调用500毫秒后执行,如此往复2.去抖2.还有一种节流方式,是通过返回闭包的形式,可以设置延迟时间,两者运行的结果是一样,但是我在实际操作的时候设置延迟500时,滚动过了一会才执行了,设置为delay为100的时候在视觉上就没有感觉延迟。而且函数也只滚动了一次。<code>function
    debounce1(method,delay){var timer = null;return function(){var
    context = this,args = arguments;clearTimeout;timer =
    setTimeout(function(){method.apply(context,args);},delay);}}</code>

很多时候业务需要让页面初始化的时候就定位到页面某个位置,前面那样简单的判断会对页头至定位目标位置之间img浪费加载。可以通过getBoundingClientRect()方法获取img相对于视口的位置,从而判断是否需要加载目标图片。

使用节流函数进行优化

如果直接将函数绑定在scroll事件上,当页面滚动时,函数会被高频触发,这非常影响浏览器的性能。

同时还有以下场景往往由于事件频繁被触发,因而频繁执行DOM操作、资源加载等重行为,导致UI停顿甚至浏览器崩溃。

1.window对象的resize、scroll事件

2.拖拽时的mousemove事件

3.射击游戏中的mousedown、keydown事件

4.文字输入、自动完成的keyup事件

解决这个问题的方法有去抖动和节流的方法

  • 去抖动原理:
    当调用动作n毫秒后,才会执行该动作,若在这n毫秒内又调用此动作则将重新计算执行时间。

不足:当我一直滚动鼠标的时候,lazyload函数就会不断被延迟,这样只有停下来的时候才会执行,那么再有些需要及时显示的情况下,就显得不那么友好了

  • 节流原理:预设一个执行周期,如果这个周期结束了都还没触发函数,那就会执行一次函数;如果这个周期还没结束就触发了函数,那定时器将重置,开始新周期。

达到了想要的效果,既没有频繁的执行也没有延迟执行

详细可看此文
关于js函数节流和去抖动

throttle

当我一直滚动鼠标的时候,lazyload函数就会不断被延迟,这样只有停下来的时候才会执行,那么再有些需要及时显示的情况下,就显得不那么友好了(对于实现keyup事件的提示也没有意义了),所以可以为函数添加一个参数作为到固定间隔必须执行,到了这个时间间隔就必须执行,这个时候就引入了节流:节流:如果将水龙头拧紧直到水是以水滴的形式流出,那你会发现每隔一段时间,就会有一滴水流出。也就是会说预先设定一个执行周期,当调用动作的时刻大于等于执行周期则执行该动作,然后进入下一个新周期代码如下:<code>function
throttle2(method, delay, time) {var timeout,startTime = new
Date();return function() {var context = this,args = arguments,curTime =
new Date();clearTimeout;// 如果达到了规定的触发时间间隔,触发 handlerif
(curTime – startTime >= time) {method.apply(context, args);startTime
= curTime;// 没达到触发间隔,重新设定定时器} else {timeout =
setTimeout(method, delay);}};</code>

奥门金沙手机娱乐网址 14

在这个函数中,当一次时间较长的时候还是会执行两次,而不是等滚动停止之后再执行。达到了想要的效果,既没有频繁的执行也没有最后执行

vareLvW,elvH,varinViewTreshhold=10;//...//inViewTreshhold值可以根据页面是否加载完动态改变大小,当页面加载完的时候增大,也可说页面负担小的时候预加载多一些eLvW=window.innerWidth+inViewTreshhold;elvH=window.innerHeight+inViewTre·shhold;eLnegativeTreshhold=inViewTreshhold*-1;rect=lazyloadElems[globalLazyIndex].getBoundingClientRect();//判断是否在可视区域if(>=eLnegativeTreshhold&&<=elvH&&>=eLnegativeTreshhold&&<=eLvW&&(eLbottom||eLright||eLleft||eLtop)){//执行加载图片动作//...}

运用节流函数的图片懒加载代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style>
        img {
            display: block;
            margin-bottom: 50px;
            width: 400px;
            height: 400px;
        }
    </style>
</head>
<body>
     <img src="" data-src="http://pic.58pic.com/58pic/17/18/97/01U58PIC4Xr_1024.jpg" alt="奥门金沙手机娱乐网址 15">
    <img src="" data-src="http://ww4.sinaimg.cn/large/006y8mN6gw1fa5obmqrmvj305k05k3yh.jpg" alt="奥门金沙手机娱乐网址 16">
    <img src="" data-src="http://ww1.sinaimg.cn/large/006y8mN6gw1fa7kaed2hpj30sg0l9q54.jpg" alt="奥门金沙手机娱乐网址 17">
    <img src="" data-src="http://cover.read.duokan.com/mfsv2/download/fdsc3/p01N203pHTU7/Wr5314kcLAtVCi.jpg!t" alt="奥门金沙手机娱乐网址 18">
    <img src="" data-src="http://77fkxu.com1.z0.glb.clouddn.com/20160308/1457402219_73571.jpg" alt="奥门金沙手机娱乐网址 19">
    <img src="" data-src="http://pic1.cxtuku.com/00/16/18/b3809a2ba0f3.jpg" alt="奥门金沙手机娱乐网址 20">
    <img src="" data-src="http://img.bitscn.com/upimg/allimg/c150708/14363B06253120-6060O.jpg" alt="奥门金沙手机娱乐网址 21">
    <img src="" data-src="http://cover.read.duokan.com/mfsv2/download/fdsc3/p015trgKM7vw/H0iyDPPneOVrA4.jpg!t" alt="奥门金沙手机娱乐网址 22">
    <img src="" data-src="http://ww1.sinaimg.cn/large/006y8mN6gw1fa7kaed2hpj30sg0l9q54.jpg" alt="奥门金沙手机娱乐网址 23">
    <img src="" data-src="http://imgsrc.baidu.com/baike/pic/item/2f9cbdcc5e0bcf5c00e9283b.jpg" alt="奥门金沙手机娱乐网址 24">
    <img src="" data-src="http://ww4.sinaimg.cn/large/006y8mN6gw1fa5obmqrmvj305k05k3yh.jpg" alt="奥门金沙手机娱乐网址 25">
<script>
    (function(){
    let num = document.getElementsByTagName('img').length;
    let img = document.getElementsByTagName("img");
    let n = 0; //存储图片加载到的位置,避免每次都从第一张图片开始遍历
    lazyload(); //页面载入完毕加载可是区域内的图片
    function lazyload() { //监听页面滚动事件
        let seeHeight = document.documentElement.clientHeight; //可见区域高度
        let scrollTop = document.documentElement.scrollTop || document.body.scrollTop; //滚动条距离顶部高度
        for (let i = n; i < num; i++) {
            // 图片未出现时距离顶部的距离大于滚动条距顶部的距离+可视区的高度
            if (img[i].offsetTop < seeHeight + scrollTop) {
                if (img[i].getAttribute("src") == "") {
                    img[i].src = img[i].getAttribute("data-src");
                }
                n = i + 1;
            }
        }
    }
    采用了节流函数
        function throttle(fun, delay, time) {
    let timeout,
        startTime = new Date();
    return function() {
        let context = this,
            args = arguments,
            curTime = new Date();
        clearTimeout(timeout);
        // 如果达到了规定的触发时间间隔,触发 handler
        if (curTime - startTime >= time) {
            fun.apply(context, args);
            startTime = curTime;
            // 没达到触发间隔,重新设定定时器
        } else {
            timeout = setTimeout(fun, delay);
        }
    };
};
window.addEventListener('scroll',throttle(lazyload,500,1000));
    })()
</script>
</body>
</html>

demo:图片懒加载性能优化

如果不对的地方,恳请指出,谢谢^ ^

引一波图片懒加载。

页面加载速度影响最大的就是图片,一张普通的图片可以达到几M的大小,而代码也许就只有几十KB。当页面图片很多时,页面的加载速度缓慢,几S钟内页面没有加载完成,也许会失去很多的用户。所以,对于图片过多的页面,为了加速页面加载速度,所以很多时候我们需要将页面内未出现在可视区域内的图片先不做加载,
等到滚动到可视区域后再去加载。这样子对于页面加载性能上会有很大的提升,也提高了用户体验。

将页面中的img标签src指向一张小图片或者src为空,然后定义data-src(这个属性可以自定义命名,我才用data-src)属性指向真实的图片。src指向一张默认的图片,否则当src为空时也会向服务器发送一次请求。可以指向loading的地址

只有当滚动到该图片的时候才将该图片的src换为data-src。这样就能加速加载速度。

奥门金沙手机娱乐网址 26这样一开始页面只需要加载一张图片奥门金沙手机娱乐网址 27滚动两次之后

参考网址:

事件节流

scroll、touchmove、resize事件会触发大量的计算,在低版本Andorid版本浏览器中卡顿甚至崩溃,我们可以简单做一些事件节流的操作。

vareLnow=Date.now();varlazyEvalLazy={vartimer,running;varunblock=function(){running=false;};varrun=function;//执行加载图片动作//...setTimeout;};return{debounce:function;running=true;timer=setTimeout;},throttled:function(){vardelay;if{running=true;clearTimeout;delay=Date.now{delay=9;}else{delay=99;}timer=setTimeout;