【转】“懒”的妙用——浅析图片懒加载技术

2,595 阅读6分钟
原文链接: mp.weixin.qq.com

1、定义

图片懒加载是一种网页优化技术。图片作为一种网络资源,在被请求时也与普通静态资源一样,将占用网络资源,而一次性将整个页面的所有图片加载完,将大大增加页面的首屏加载时间。为了解决这种问题,通过前后端配合,使图片仅在浏览器当前视窗内出现时才加载该图片,达到减少首屏图片请求数的技术就被称为“图片懒加载”。

2、传统的解决方案

我们先来探讨一下传统的图片懒加载技术是如何实现的。

以京东sale活动页为例,我们随便选取一个活动地址,查看网页源代码,我们可以找到形如这种结构的img标签:

<img class="J_imgLazyload" src="//img14.360buyimg.com/cms/g10/M00/13/04/rBEQWFFj4PUIAAAAAAAESxyqJLUAADvdAIHC9oAAARj186.gif" original="//img11.360buyimg.com/cms/jfs/t12118/41/1394617476/43413/2253395a/5a1f7569N63f38100.jpg" />

我们试着来分析一下这个img标签的结构,首先我们在浏览器中直接打开该img标签的src,即:“//img14.360buyimg.com/cms/g10/M00/13/04/rBEQWFFj4PUIAAAAAAAESxyqJLUAADvdAIHC9oAAARj186.gif”。结果发现这张图是一张10*10的空白图片。我们发现img标签中还有一个伪属性original,里面也有一个很像是图片的链接,我们再在浏览器中访问这个链接:

果然如我们所料,original中存放的才是真实图片的路径。

那么浏览器是怎么加载存放在伪属性中的真实图片的呢?

首先后端直出的页面结构中,img标签中的src为一张占位图,真实图片地址存放在一个伪属性中,如:data-src中。当页面滚动时,遍历当前页面需要进行懒加载的图片,判断图片是否在可视区域内,如果在的话,则取存放在伪属性中的真实src替换当前的src。

以上是一张简略的示意图,外部黑色框代表页面范围(即html的范围),虚线内代表浏览器当前视窗区域。在这个场景下,只有第4、5、6张图片处在当前视窗中,因此它们会使用上述加载策略,使用真实src替换占位图。而其余的几张图片由于并不在可视区域内,因此并不会被立即加载,只有当滚动事件产生,某张图片(或图片的一部分)开始出现在视窗内时,才会真正加载。

3、可优化的一些点:

(1)滚动函数的节流

浏览器中的滚动事件是一个触发非常频繁的事件,有时候我们主观体验只是轻轻拨动了一下滚轮,但实际上触发了数次甚至数十次的onscroll事件:

还记得我们在前文所述的懒加载策略中提到,每次滚动事件都将执行:选择页面所有图片→遍历→筛选在当前视窗内的图片→使用真实src替换占位图。以上过程是一个非常耗时的过程,尤其是在筛选符合条件的图片的步骤。因此,我们需要对滚动事件进行节流:只有当后一次触发滚动与前一次触发滚动的时间间隔大于一定阈值时,才认为这次滚动生效。

通俗地说,就是两次滚动事件之间必须大于一定的时间。这意味着用户在快速下滑页面时,筛选加载策略并不会执行,只有当用户停下或是逐渐放缓下滑速度时,才会进行筛选加载策略。若用jQuery实现上述节流步骤则如下所示(scroll事件节流阈值150ms,resize事件节流阈值100ms):

(2)预加载

顾名思义,预加载就是指预先加载一些未在当前视窗内,但是即将出现的一些图片。还是拿前文举的例子进行扩展:

可以看到,虽然当前视窗中只有3张图片,但若使用预加载,将提前加载前后各1屏幕内的图像(红色虚线内),如此一来,当用户在当前视窗内继续上滑或下滑时,该屏内的图片已经提前加载完毕,用户就不会感知到图片从白屏到显像的这种抖动过程,大大提升了用户体验。

4、更优的用户体验

在国内网站中,知乎官网在图片懒加载部分做得较为领先,拥有很好的用户体验。让我们先来看一下效果,随便打开一个知乎问答页(在PC环境中打开):知乎问答页示例

效果图:

仔细观察,我们可以看到这样一种效果:图片在由白至清晰的过程中有一种模糊渐变的效果,相比于直接替换空白占位图的方案,这种方式不会给用户带来突兀感。

查看页面源代码,我们发现直出的页面中img标签是以这种形式存在的:

img标签及其内的信息作为字符串保存在noscript标签中,src中为一张低品质图片,data-original中为真实图片地址,同时有一个占位的span元素。当页面滚动到该noscript标签对应的位置时,解析出noscript标签中的img,并替换占位span元素:

此时将先加载低质量的小图,再加载清晰的高质量原图:

如此一来,用户将在极短的时间内就能看到图片的大致轮廓。随后等真实图片加载完毕后,再使用CSS控制图片逐渐变清晰,整个过程顺畅自然,一气呵成。在网速较差的环境下,这种方式也能有很优雅的降级体验。

5、更加富有脑洞的图片懒加载策略:

这里有一篇国外论坛的文章:How to use SVG as a Placeholder, and Other Image Loading Techniques,主题是利用SVG生成各种风格的占位图。国内也有人对其进行了简要翻译:图片加载时使用 SVG 作为图片 placehold。

我们在上文中提到的占位图都是一个空白图片的形式,而这篇文章中提出了一些非常新颖而前卫的占位图生成方式:

(1)梯度底色占位图:

这种方式是通过后端的处理,将img标签中的图片提前做一次解析,处理为一种带梯度的SVG元素,这种SVG元素所占体积极小,可以随页面一起直出。

换种思路,如果我们不想使用SVG,也可以直接将图片的主要背景颜色提取出来,使用base64方式直出该图片。

(2)SVG色块:

我们可以通过一些简单的SVG元素拼接,描绘出图片的基本轮廓,并在此基础上加上一个模糊滤镜,也可以达到很好的占位图效果。

(3)二值化剪影:

效果图如上。

推荐看一下原文,作者也分享了一些生成这些SVG占位图所需要使用的工具库。