网页性能优化:延迟加载资源

延迟加载

延迟加载是一种在加载页面时,延迟加载非关键资源的一种方法,而这些非关键资源则在需要时才进行加载,就图像而言,‘非关键’通常指的是‘屏幕外’。

  • 延迟加载图像和视频时,可以减少初始页面加载时间、初始页面负载以及系统资源使用量,所有这一切都会对性能产生积极影响。
  • 通常来说加载网页时,浏览器会请求所有的图像,而不管它们实在视窗还是在页面的更深处并且不可见。延迟加载允许我们加载可见的图像,并且在用户滚动时按需异步加载其他的图片。这减少了负载请求的数据,并可以大大加快该过程。

延迟加载图像

HTML中的延迟加载内联图像

  1. <img>元素中使用的图像时最常见的延迟加载对象。延迟加载<img>元素时,使用javascript来检查其是否在视口中,如果元素在视口中,则其src(有时srcset)属性中就会填充所需图像内容的网址。

  2. 使用 Intersection Observer。 之前写过的延迟加载代码,有的是使用scroll或resize等时间处理程序来完成任务。这种方式的有点就是各浏览器之间的兼容性最好。但现在浏览器支持通过Intersection Observer API 来检查元素的可见性。这中方式的性能和效率更好。当然并非所有的浏览器都支持Intersection Observer

与依赖于各种事件处理程序的代码相比,Intersection Observer更容易使用和阅读。只需要注册一个Observer即可监听视元素,就不需要编写冗余的元素可见性检测代码。只需要决定元素可见时需要做什么即可。

<img src alt="I'm an image!">

需要关注的三部分:

  1. class,javascript中选择元素时需要使用的类选择器。
  2. src,引用页面最初加载时显示的占位符图片。
  3. data-src和data-srcset,属于占位属性,其中包含元素进入视口后要加载的图片的网址。

接下来使用Intersection Observer实现延迟加载图片。

document.addEventListener('DOMContentLoaded',function(){
      const lazyImages = [].slice.call(document.querySelectorAll("img.lazy"));

        if ("IntersectionObserver" in window && "IntersectionObserverEntry" in window && "intersectionRatio" in window.IntersectionObserverEntry.prototype) {
            let lazyImageObserver = new IntersectionObserver(function(entries, observer) {
                entries.forEach(function(entry) {
                    if (entry.isIntersecting) {
                        let lazyImage = entry.target;
                        lazyImage.src = lazyImage.dataset.src;
                        lazyImage.classList.remove("lazy");
                        lazyImageObserver.unobserve(lazyImage);
                    }
                });
            });

            lazyImages.forEach(function(lazyImage) {
                lazyImageObserver.observe(lazyImage);
            });
        }else{
        //  对不支持Intersection Observer 其他处理方式
    }
})

DOMContentLoaded事件中,脚本会查询DOM来获取类属性为lazy的所有<img>元素。

实现效果

Intersection Observer来做延迟加载,但是对兼容性要求严格,可以使用polyfill来做兼容处理。但是也可以使用scrollresize的代码,getBoundingClientRect配合使用的orientationchange事件处理程序,来确定元素是否在视口中。

document.addEventListener("DOMContentLoaded", function() {
  let lazyImages = [].slice.call(document.querySelectorAll("img.lazy"));
  let active = false;

  const lazyLoad = function() {
    if (active === false) {
      active = true;

      setTimeout(function() {
        lazyImages.forEach(function(lazyImage) {
          if ((lazyImage.getBoundingClientRect().top <= window.innerHeight && lazyImage.getBoundingClientRect().bottom >= 0) && getComputedStyle(lazyImage).display !== "none") {
            lazyImage.src = lazyImage.dataset.src;
            lazyImage.srcset = lazyImage.dataset.srcset;
            lazyImage.classList.remove("lazy");

            lazyImages = lazyImages.filter(function(image) {
              return image !== lazyImage;
            });

            if (lazyImages.length === 0) {
              document.removeEventListener("scroll", lazyLoad);
              window.removeEventListener("resize", lazyLoad);
              window.removeEventListener("orientationchange", lazyLoad);
            }
          }
        });

        active = false;
      }, 200);
    }
  };

  document.addEventListener("scroll", lazyLoad);
  window.addEventListener("resize", lazyLoad);
  window.addEventListener("orientationchange", lazyLoad);
});

此代码在 scroll 事件处理程序中使用 getBoundingClientRect 来检查是否有任何 img.lazy 元素处于视口中。 使用 setTimeout 调用来延迟处理,active 变量则包含处理状态,用于限制函数调用。 延迟加载图像时,这些元素随即从元素数组中移除。 当元素数组的 length 达到 0 时,滚动事件处理程序代码随即移除。 您可在此 CodePen 示例中,查看代码的实际运行情况。

css中的图像

将通过设置css的background-color属性了调用图片,预加载时不考虑可见性的<img>不同,css中的图片加载行为是建立在更多推测之上。构建文档和CSSOM以及渲染树后,浏览器会先检查CSS以何种方式适应于文档,再请求外部资源。吐过浏览器确定设计某外部资源成的CSS规则不适用于当前构建的文档,则浏览器将不会请求该资源。

这种推测性行为来延迟CSS中的图片加载,方法是使用Javascript来确定元素在视口内,然后将一个类应用于该元素,已应用背景图像的样式。

<div>
  <h1>Here's a hero heading to get your attention!</h1>
  <p>Here's hero copy to convince you to buy a thing!</p>
  <a href="/buy-a-thing">Buy a thing!</a>
</div>

div.lazy-background元素通常包含有某些CSS调用的大型背景图片。在延迟加载中,可以通过visible来隔离div.lazy-background元素的background-color属性。当元素进入视口时再对其添加这个类。

.lazy-background {
  background-image: url("hero-placeholder.jpg"); /* Placeholder image */
}

.lazy-background.visible {
  background-image: url("hero.jpg"); /* The final image */
}

通过元素是否在视口内(Intersection Observer),如果在视口内,则对 div.lazy-background元素添加visible类加载图像;

document.addEventListener("DOMContentLoaded", function() {
  var lazyBackgrounds = [].slice.call(document.querySelectorAll(".lazy-background"));

  if ("IntersectionObserver" in window) {
    let lazyBackgroundObserver = new IntersectionObserver(function(entries, observer) {
      entries.forEach(function(entry) {
        if (entry.isIntersecting) {
          entry.target.classList.add("visible");
          lazyBackgroundObserver.unobserve(entry.target);
        }
      });
    });

    lazyBackgrounds.forEach(function(lazyBackground) {
      lazyBackgroundObserver.observe(lazyBackground);
    });
  }
});

延迟加载视频

在正常情况下加载视屏时,我们使用的是<video>元素。

视频不需要自动播放

使用度量none的preload属性来阻止浏览器预加载任何视频数据。poster属性提供占位符。

<video controls preload="none">
  <source src="one-does-not-simply.webm" type="video/webm">
  <source src="one-does-not-simply.mp4" type="video/mp4">
</video>

视频代替GIF

虽然动画GIF应用广泛,但其在很多方面的表现均不如视频,尤其是在输出文件大小方面。动画GIF的数据大小可达数兆字节,而视觉效果相当的视频往往很小。

gif图片加载时会自动播放,并且会循环播放而且没有声音。

使用 <video> 元素进行替代类似于:

<video autoplay loop>
  <source src="one-does-not-simply.webm" type="video/webm">
  <source src="one-does-not-simply.mp4" type="video/mp4">
</video>

autoplay、muted 和 loop 属性的含义不言而喻,而 playsinline 是在 iOS 中进行自动播放所必需。可以跨平台使用的“视频即 GIF”替代方式。Chrome 会自动延迟加载视频,但并不是所有浏览器都会提供这种优化行为。

<video autoplay loop width="610" height="254">
  <source data-src="one-does-not-simply.webm" type="video/webm">
  <source data-src="one-does-not-simply.mp4" type="video/mp4">
</video>

添加了 poster 属性,您可以使用该属性指定占位符以占用 <video> 元素的空间,直到延迟加载视频为止。 与上文中的 <img> 延迟加载示例一样,我们将视频网址存放在每个 <source> 元素的 data-src 属性中。 然后将使用与上文基于 Intersection Observer 的图像延迟加载示例类似。

document.addEventListener("DOMContentLoaded", function() {
  var lazyVideos = [].slice.call(document.querySelectorAll("video.lazy"));

  if ("IntersectionObserver" in window) {
    var lazyVideoObserver = new IntersectionObserver(function(entries, observer) {
      entries.forEach(function(video) {
        if (video.isIntersecting) {
          for (var source in video.target.children) {
            var videoSource = video.target.children[source];
            if (typeof videoSource.tagName === "string" && videoSource.tagName === "SOURCE") {
              videoSource.src = videoSource.dataset.src;
            }
          }

          video.target.load();
          video.target.classList.remove("lazy");
          lazyVideoObserver.unobserve(video.target);
        }
      });
    });

    lazyVideos.forEach(function(lazyVideo) {
      lazyVideoObserver.observe(lazyVideo);
    });
  }
});

延迟加载 <video>元素时,我们需要对所有的 <source>子元素进行迭代,并将其 data-src 属性更改为 src 属性。 完成该操作后,必须通过调用该元素的 load 方法触发视频加载,然后该媒体就会根据 autoplay 属性开始自动播放。

延迟加载库

参考链接

欢迎访问主页,有更多文章内容
转载请注明原出处
原文链接地址:网页性能优化:延迟加载资源

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,294评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,493评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,790评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,595评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,718评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,906评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,053评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,797评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,250评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,570评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,711评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,388评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,018评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,796评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,023评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,461评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,595评论 2 350