注明:本人原创翻译,原版为Essential Image Optimization电子书,这里将其拆分为几篇文章发布。另外,文中部分链接可能会因为“网络”原因无法打开。不必着急,我会慢慢将其中一些比较好的内容翻译过来发表,都会在这个“Web图像技术深究”专题中。
目录
- 介绍
- 如何判断我的图像是否需要优化?
- 如何选择正确的图像格式?
- “素人”JPEG
- JPEG的压缩模式
-
什么是WebP?
- WebP的表现如何?
- 谁在生产环境中使用WebP?
- WebP编码如何执行?
- WebP的浏览器支持
- 如何将我的图像转换为WebP?
- 如何在我的操作系统上查看WebP图像?
- 如何提供WebP?
- SVG优化
- 避免使用有损编解码器重复压缩图像
-
减少不必要的图像解码和尺寸调整带来的损耗
- 使用srcset提供HiDPI图像
- 艺术化的响应
- 颜色管理
- 图像拼合技术
- 延迟加载非关键图像
- 避免<code>display: none;</code>的陷阱
- 图像CDN服务对你有意义吗?
- 缓存图像资源
- 预加载关键图像资源
- 图像的网络性能预算
- 最后的建议
- 附注
正文:
图像拼合技术
图像拼合技术 (或者说CSS图像拼合技术)在Web开发上其实有着悠久的历史,它受到所有浏览器的支持,这个技术是通过加载单个更大的合并图像来减少页面加载图像数量的一种流行方式。
图像拼合仍然广泛应用于很多大型网站的生产环境中,包括Google主页。
在HTTP/1.x下,一些开发人员通过合并来减少加载图像的HTTP请求数量。这带来了一些好处,但是它很快遇到了“缓存无效”的挑战,需要小心的是 - 对于图像的任何的一小部分的更改,都将使得用户的缓存中的整个图像无效。
现在,拼合技术在HTTP/2可能是不合适的。使用HTTP/2的话,最好是加载单个图像,因为现在单个链接中的多个请求是可能的。因此,使用之前请评估一下,图像拼合技术是否适用于你的网络设置。
延迟加载非关键图像
延迟加载是一种优化Web性能的模式,它会延迟浏览器中图像的加载,直到用户需要看到它的时候。一个例子是,当您滚动页面时,可视区域的图像会按需异步加载。这可以进一步帮助你节省从图片压缩策略省出来的带宽空间。
出现在“折叠线之上”或网页中首次出现的图像必须立即加载。然而,在“折叠线下方”的图像对于用户来说尚不可见,它们不必被立即加载到浏览器中。只有当用户向下滚动并且需要显示它们时,再稍后加载或者延迟加载即可。
其实,浏览器本身并没有支持延迟加载(尽管过去曾经讨论过)。因此,我们会使用JavaScript来添加此功能。
为什么延迟加载是有效的?
这种所谓“延迟”,即只有在必要时加载图像的方式,有很多好处:
- 降低数据消耗:由于你假设用户不需要提前提取每个图像,因此您只需加载最少的资源。这永远是一件好事,特别是在具有更严格的流量控制的移动设备上。
- 降低电池消耗:减少了用户浏览器的工作量,也可以节省电池的使用寿命。
- 提高下载速度:将图像占比较大的网站的整体页面加载时间,从几秒缩短到几乎没有,这是对用户体验的巨大提升。事实上,这可能是决定你的用户在你的网站长期驻留还是昙花一现的关键点。
但是,就像所有的工具一样,强大的力量意味着巨大的责任。**
避免在折叠线之上放置图像。延迟加载可以用于长列表的图像(例如产品)或用户头像列表中。但不要用于主页的焦点图像。延迟加载折叠线以上的图像将会使整个页面的加载显得很慢,无论是从技术上还是人类的感知上讲。我们可以使用JavaScript屏蔽浏览器的预加载、增加逐步加载,为浏览器创建额外的工作。
在滚动时延迟加载图像,要非常谨慎。如果你等待用户滚动之后再加载图像,他们可能就会看到图像占位符,并且很有可能在它们已经滚动过去后图像才加载完成。一个建议就是在加载折叠线之上的图像后就开始延迟加载,加载所有图像,而不依赖于用户的操作。
谁在使用延迟加载技术?
有关延迟加载的示例,请查看以托管大量图像为主营业务的所有站点。比较著名的网站是Medium和Pinterest。
在Medium.com上,一个使用高斯模糊在线预览的例子。
一些网站(例如“Medium”)会先显示一个小的高斯模糊的在线预览图像(几百个字节),一旦获取完成,它将转换(延迟加载)到全质量的图像。
JoséM.Pérez曾经写过关于如何使用CSS过滤器实现Medium网站的类似效果,并尝试了不同的图像格式来支持这样的占位符。Facebook也曾为其著名的200字节方式占位符,撰写了一份值得一读的文章“封面照片”。如果你是Webpack的使用者,LQIP加载程序可以帮助你自动完成类似的工作。
事实上,您可以搜索你最喜欢的高清照片来源,然后向下滚动页面。几乎所有情况下,你都将体验到网站一次只能加载几个全分辨率的图像,其余的则是占位符颜色或图像。当你继续滚动时,占位符图像将被替换为全分辨率图像。这就是典型的延迟加载效果。
如何在我的页面使用延迟加载?
有一些技术和插件可支持延迟加载。我推荐Alexander Farkas编写的lazysizes,因为它的性能良好、功能齐全、并可选择通过浏览器的“交叉观察者”API(IntersectionObserver API)集成,还支持插件扩展。
我可以怎样使用Lazysizes?
Lazysizes是一个JavaScript的库。它不需要配置,只需下载压缩后的js文件并将其包含在您的网页中即可使用。
这是从Lazysizes的README文件中获取的一些示例代码:
将CSS类“lazyload”添加到你的包含data-src或data-srcset属性的<img>
或<iframe>
标签上。
或者,您也可以添加指向低质量图像的src属性:
<!-- 非响应式示例: -->
![](image.jpg)
<!-- 响应式示例,包括data-sizes为auto: -->
<img
data-sizes="auto"
data-src="image2.jpg"
data-srcset="image1.jpg 300w,
image2.jpg 600w,
image3.jpg 900w" class="lazyload" />
<!-- iframe示例 -->
<iframe frameborder="0"
class="lazyload"
allowfullscreen=""
data-src="//www.youtube.com/embed/ZfV-aYdU4uE">
</iframe>
实际上,在这本书的网络版本中,我将Lazysizes(尽管可以使用任何替代方案)与Cloudinary配合响应式地返回需求的图像。这允许我自由地尝试不同的尺度、质量、格式的值,而无需在响应式处理上付出很大的精力:
Lazysizes的功能包括:
- 自动检测当前和未来的“lazyload”元素的可见性变化。
- 包括标准的响应图像支持(包括picture和srcset)。
- 添加多媒体列表功能的自动尺寸计算和别名。
- 可被用在CSS和JS使用比重较大的网页或网络应用程序上,处理数以百计的images或 iframe标签。
- 可扩展:支持插件
- 轻量级但成熟的延迟加载解决方案
- SEO已改进:不能在爬虫抓取时隐藏图像或资源。
其他延迟加载的可选项
Lazysizes并不是你唯一的选择。这里还有其他的延迟加载库:
- Lazy Load XT
- BLazy.js (或[Be]Lazy)
- Unveil
- yall.js (另一个延迟加载器) 它只有1KB,并且同样支持浏览器“交叉观察者”API。
延迟加载有什么问题么?
- 一些阅读器、搜索机器人和任何禁用JavaScript的用户将无法看延迟加载的图像。但是,我们可以通过一个
<noscript>
标签来提示解决! - 对页面滚动进行侦听,例如确定何时加载延迟加载的图像,可能会对浏览器的滚动性能产生不利影响。可能会导致浏览器重绘多次,从而减缓网络爬取的进程 - 但是,智能的延迟加载库将会使用节流阀(throttling)来缓解这种情况。一个可能的解决方案是浏览器的“交叉观察者”API,lazysizes是支持这种模式的。
延迟加载图像是减少带宽占用、降低加载消耗和改善用户体验的一个广泛使用的方式。你可以评估它,对你网站的用户体验是否有意义。需要进一步了解,可以参阅延迟加载图像和实现Medium的渐进式加载。
避免display:none的陷阱
一些陈旧的响应式图像解决方案,会错误地理解当设置CSS display
属性时浏览器处理图像请求的方式(以为display:none
的时候就不会请求获取图像)。这可能会是你比你所期望的请求了更多的图像的另一个原因,所以<picture>
和<img srcset>
依然是你响应式加载图像的首选方式。
你有没有写过一种@Media
语句,它在某些断点上将图像设置为display:none
?
![](img.jpg)
<style>
@media (max-width: 640px) {
img {
display: none;
}
}
</style>
或者是使用display:none
的CSS设置图像隐藏切换?
<style>
.hidden {
display: none;
}
</style>
![](img.jpg)
<img src=“img-hidden.jpg" class="hidden">
一个快速验证的方法,就是使用Chrome开发者工具的网络面板,可以看到上述方法想要隐藏的图像其实仍然会被获取,即使我们的期望是它们不会被访问。根据嵌入式资源规范,浏览器的这种行为其实是没有问题的。
使用display:none
会避免触发图像src
指定的链接请求?
<div style="display:none">![](img.jpg)</div>
答案是否定的。指定的图像仍然将被请求获取。样式display:none
无法影响这个请求,因为在JavaScript可以修改src之前,图像请求就已经发出了。
那么使用display:none
会避免触发图像background: url()
指定的链接请求么?
<div style="display:none">
<div style="background: url(img.jpg)"></div>
</div>
这回答案是肯定的。 上面编写的元素一旦被解析,其中的CSS背景图片就不会被请求获取。CSS样式解析计算时会认为,带有display:none
元素的任何子元素都是没有作用的,对于整个文档的显示没有影响。所以,子元素中的任何背景图片都是会被忽略并不被下载的。
Jake Archibald的请求任务解析中对于你响应式图像加载中使用display:none
的陷阱有一个很好的测验。当你怀疑你的浏览器是如何处理图像请求加载时,可以打开它的开发者工具并自己验证图像加载的情况。
再次提醒你,在可能的情况下,还是建议使用<picture>
和<img srcset>
来处理,而不是依赖于display:none
。
预加载关键图像资源
对于关键图像资源的预加载,可以使用 <link rel=preload>
.
<link rel=preload>
是一个声明式的提取,允许你强制浏览器对资源进行请求,而不会阻止页面文档的onload
事件。它可以增加资源请求的优先级,防止在文档解析的后期可能无法找到资源。
可以通过指定 as
的值 image
来预加载图像文件:
<link rel="preload" as="image" href="logo.jpg"/>
<img>
,<picture>
,srcset
和SVGs中使用的图像资源,都可以采用这种方式优化加载。
注意: 支持
<link rel="preload">
浏览器包括Chrome和其他基于Blink的浏览器如Opera、Safari的技术预览版,Firefox也已经增加了支持。
像飞利浦、FlipKart和施乐等网站都是使用<link rel=preload>
来预加载其主要的徽标资源(通常在页面载入的早期)。Kayak也使用了预加载,以确保其页面顶部的焦点图像尽快加载出来。
HTTP头的预加载是什么?
可以使用HTML标签或者页面<header>
的Link中指定预加载链接。在任一种情况下,预加载链接都会指示浏览器开始将资源加载到内存高速缓存中,指明该页面需要高效率的使用此资源,并且不希望等待页面加载或解析器发现它时再获取。
一个<header>
中的预加载连接设置,就像下面这样:
Link: <https://example.com/logo-hires.jpg>; rel=preload; as=image
当“金融时报”在他们的网站网页的头部设置了一个链接预加载之后,显示他们头条图像所花费的时间被减少了1秒钟:
<link rel=preload>
,顶部:没有使用。WebPageTest使用Moto G4在3G环境下对“金融时报”首页之前和之后的载入时间比较。
同样地,维基百科也通过使用预加载链接技术改善了他们徽标的载入时间表现,可以通过他们的此案例研究中的介绍看到。
使用预加载时有哪些注意事项?
首先,你要非常确定指定的图像资源是非常值得被预先加载的;如果它们对你的用户体验不是至关重要的,那么页面上可能会有其他内容需要重点关注要预先加载。通过优先处理图像请求,您可能最终会将其他资源进一步放置到队列中。
一个非常需要注意的是,要避免使用rel=preload
预加载那些不被浏览器广泛支持的图像格式(例如WebP)。最好还要避免将其用于使用srcset
设置的根据设备条件而变化图像链接位置的响应式图像。
想要了解更多的预加载相关的信息,请查阅一下文章:Chrome中的预加载、预提取和优先级和预加载:究竟好在哪里?。
最后一篇:【译】重要的图像优化之十:写在最后