通过“延迟加载”图片优化网站性能

蒲公英 提交于 周二, 04/30/2019 - 21:39

学习如何通过延迟图像加载提高你的站点性能。

最近,我花了些时间改进站点性能。在之前关于这个主题的博客文章中,我描述了在站点上优化JavaScript和CSS的情况,并得出结论,下一步是图像优化。

去年夏天,我发表了一篇我在阿卡迪亚国家公园度假的文章。文章中包含了13张照片,总大小约4mb。

当我用https://webpagetest.org对这篇文章进行基准测试时,显示渲染页面花费了7.275秒(蓝色竖线)。

该图显示呈现页面时浏览器下载了所有13张图片。为什么浏览器会下载所有图片,如果大多数都折叠在下方,用户滚动页面时再显示不是更好吗?一起显示出来没多大意义。

从图中看出,下载全部13张图片需要很长时间(紫色的水平线)。在优化图片加载方式之前,无论如何优化CSS和JavaScript,这篇文章的速度都很慢。

性能

“延迟加载”图像是解决这个问题的一个方案。延迟加载意味着在用户滚动并将图像导入浏览器的视图之前,不会加载图像。

你可能在Facebook、Pinterest或Medium等网站上看到过惰性加载。通常是这样的:

  • 你可以像往常一样访问页面,滚动内容。

  • 你看到的不是实际图像,而是模糊的占位符图像。

  • 然后,占位符图像与最终图像尽快交换。

性能

为了在我的博客上支持延迟加载图片,我做了三件事:

  1. 自动生成轻量级可用的占位符图像;

  2. 在HTML中直接嵌入占位符图像以提高性能;

  3. 当占位符图像变得可见时,用真实图像替换它们。

生成轻量级占位符图像

为了生成轻量级占位符图像,我实现了Facebook使用的一种技术:创建一个原始图像的缩小图像,去掉图像的元数据以优化其大小,并让浏览器将图像放大。

为了创建轻量级占位符图像,我将原始图像的大小调整为5像素宽。因为我的博客上有大约1万张图片,我的drupal网站为我实现了自动化。下面是使用ImageMagickconvert工具从命令行创建一张图片的方法:

$ convert -resize 5x -strip original.jpg placeholder.jpg

  • --resize 5x 调整图像为5像素宽,同时保持其纵横比。

  • -strip 删除图像中的所有注释和冗余标题。这有助于使图像的文件大小尽可能小。

产生的占位符图像很小——通常小于400字节。

性能

我们需要为其生成占位符的原始图像。

性能

浏览器把5px的小图片放大,这个占位符图片只有395字节。

下面是另一个例子说明占位符中的颜色如何很好地匹配了原始图像:

性能

性能

尽管占位符图像应该尽可能少的显示内容,但让它们具有相关性是一种很好的方式,因为它们暗示了将要发生的事情。这也是很重要的一点,因为用户对web上的加载时间非常不耐烦

直接在HTML中嵌入占位符图像

<img>元素一个不太为人所知的特性是你可以使用数据URL模式将图像直接嵌入HTML文档中。

<img src="data:image/jpg;base64,/9j/4AAQSkZJRgABAQEA8ADwAAD/2wB DAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQICAQECAQEB AgICAgICAgICAQICAgICAgICAgL/2wBDAQEBAQEBAQEBAQECAQEBAgICAgICA gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgL/wA ARCAADAAUDAREAAhEBAxEB/8QAFAABAAAAAAAAAAAAAAAAAAAACf/EAB8QAAM AAQMFAAAAAAAAAAAAAAECAwcEBQYACAkTMf/EABUBAQEAAAAAAAAAAAAAAAAA AAIG/8QAJBEAAQIFAgcAAAAAAAAAAAAAAQURAAIDBDEGFBIVQUVRcYH/2gAMA wEAAhEDEQA/AAeyb5HO8o8lSLZd01Jz2nbKoK4yxDVvZqYl7uaV4CWdmZQSSS ST86FJBsafEJK15KD05ioNk4G6Yeg0V9bVCmZpXt08sB2hJ8DJ2Tn7H/2Q==" />

数据URLS由四部分组成:data:前缀、表示数据类型(image/jpg)的媒体类型,一个可选的base64token 、表示数据为base64编码,和base64编码的图像数据本身。

data:[<media type>][;base64],<data>

从命令行对图像进行base64编码,使用:

$ base64 placeholder.jpg

在PHP中对图像进行base64编码,可以使用:

$data = base64_encode(file_get_contents('placeholder.jpg'));

使用数据URL嵌入base64编码图像的优点是什么?它消除了HTTP请求,因此浏览器不必设置新的HTTP连接来下载图像。较少的HTTP请求意味着更快的页面加载时间。

用真实图像替换占位符图像

接下来,当占位符图像进入浏览器的视图时,我使用JavaScript的IntersectionObserver将其替换为实际图像。我遵循了Jeremy Wagner谷歌Web Fundamentals Guide上分享的延迟加载图像的方法 —— 做了一些调整。

它从以下HTML标记开始:

<img class="lazy" src="placeholder.jpg" data-src="original.jpg" />

三个相关部分是:

  1. class="lazy"属性,JavaScript使用这个选择元素。

  2. src属性,引用第一次加载页面时将出现的占位符图像。我没有链接到placeholder.jpg,而是使用上面介绍的数据URL技术嵌入图像数据。

  3. data-src属性,它包含指向原始图像的URL,当占位符进入焦点时,该URL将替换占位符。

接下来,我们使用JavaScript的IntersectionObserver将占位符图像替换为实际图像:

document.addEventListener('DOMContentLoaded', function() { var lazyImages = [].slice.call(document.querySelectorAll('img.lazy')); if ('IntersectionObserver' in window) { let lazyImageObserver = new IntersectionObserver( function(entries, observer) { entries.forEach(function(entry) { if (entry.isIntersecting) { let lazyImage = entry.target; lazyImage.src = lazyImage.dataset.src; lazyImageObserver.unobserve(lazyImage); } }); }); lazyImages.forEach(function(lazyImage) { lazyImageObserver.observe(lazyImage); }); } else { // For browsers that don't support IntersectionObserver yet, // load all the images now: lazyImages.forEach(function(lazyImage) { lazyImage.src = lazyImage.dataset.src; }); } });

Javascript使用lazy类查询所有<img>元素。当img.lazy元素进入视图时IntersectionObserver被用于把占位符图片替换为真实图片。当IntersectionObserver不被支持时,图片在DOMContentLoaded 事件中被替换。

默认情况下,IntersectionObserver的回调在图像的单个像素进入浏览器的视图时触发。但是,使用rootMargin属性,可以在图像进入视图之前触发图像替换。这减少或消除了将占位符图像替换为实际图像时的视觉或感知延迟时间。

我在我的网站上像下面这样实现:

const config = { // If the image gets within 250px of the browser's viewport, // start the download: rootMargin: '250px 0px', }; let lazyImageObserver = new IntersectionObserver(..., config);

延迟加载图像大大提高了性能

在对我的站点做了这些更改之后,我运行了一个新的https://webpagetest.org基准测试:

性能

你可以清楚地看到,页面渲染速度大大加快:

  • 文档在0.35秒(蓝色竖线)后完成,而不是原来的7.275秒。

  • 在文档完成之前没有加载任何图像,而在此之前加载了13幅图像。

  • 文档完成后,下载一个图像(紫色的水平线)。这是由JavaScript代码触发的,因为其中一个图像位于折叠上方。

延迟加载图像通过减少HTTP请求的数量提高了web页面的性能,从而减少了呈现初始页面所需下载的数据量。

base64编码图像不利于SEO吗?

更快的网站有SEO优势,因为页面速度是搜索引擎排名的一个因素。但是,延迟加载可能也不利于SEO,因为搜索引擎必须能够发现原始图像。

为了找到答案,我去了谷歌搜索控制台。谷歌搜索控制台有一个“URL inspection”功能,允许你通过Googlebot的眼睛查看网页。

我用阿卡迪亚国家公园度假这篇文章进行了测试。正如你在截图中看到的,博客文章中的第一张照片没有加载。Googlebot似乎不支持图像的数据URLs 。

性能

IntersectionObserver不利于SEO吗?

真正的问题是Googlebot是否会滚动页面、执行JavaScript、用实际图像替换占位符并索引它们。如果是,Googlebot不理解数据URLs也没关系。

为了找到答案,我决定做一个实验。昨天,我发表了一篇关于我和Matt Mullenweg一起参观博物馆的博文。该博客文章中的图像是惰性加载的,只有当爬虫程序执行JavaScript并滚动页面时,谷歌才能发现这些图像。如果这些图片出现在谷歌的索引中,我们就知道没有SEO影响。

我不确定谷歌需要多长时间才能在其索引中提供新的帖子和图片,但我会密切关注它

如果图片没有出现在谷歌的索引中,延迟加载可能会影响你的SEO。我的解决方案是只对最重要的图像有选择地禁用延迟加载。(注:即使谷歌找到了这些图片,也不能保证谷歌会决定将它们编入索引 —— 谷歌的索引中通常不包括简短的博客文章和图片。)

结论

延迟加载图像通过减少呈现初始页面所需的HTTP请求和数据的数量来提高web页面的性能。

理想情况下,随着时间的推移,浏览器将支持本地延迟加载图像,一些SEO挑战将不再是一个问题。在此之前,可以考虑自己添加对延迟加载的支持。对于我自己的站点,大约需要40行JavaScript代码和20行额外的PHP/Drupal代码。

我希望通过分享我的经验,更多的人被鼓励运行他们自己的网站,优化他们的网站性能。

原文

Blog tags