图片的优化

特别声明:如果您喜欢小站的内容,可以点击申请会员进行全站阅读。如果您对付费阅读有任何建议或想法,欢迎发送邮件至: airenliao@gmail.com!或添加QQ:874472854(^_^)

图片在 Web 中的应用是非常广泛的,而且占用的资源相比于 HTML、CSS 和 JavaScript 资源要大得多。我采用 HTTPArchive 对 Top1000000 (2021-01-01~2021-08-01)做了一个统计

同等基数,把图片相关的信息拿出来

聚划算来说,其中图片占了大约 730.4 KB

图片在 Web 中除了占有重要的一席之地之外,对资源的使用也是非常的大,而且对用户的体验影响也非常大。虽然 <img> (或 CSS 中其他能用<image> 数据类型的属性,比如 background-imagemask-imageborder-image 以及 list-style-image 等)看上去不是很起眼,但图片使用的好坏直接会影响用户的体验,Web 的性能(加载,渲染相关的性能)。或者简单地说,它直接影响 CWV (Core Web Vitals)的几个关键指标,比如 LCPCLSFID

为什么说图片会对 LCP、CLS 和 FID 有着直接影响呢?我们先从 <img> 的使用开始,然后逐步来分析。

图片是很难的

对于很多 Web 开发者而言,把图片放到一个 Web 页面(或 Web 应用)中太简单了。使用 <img> 标签,并且通过 src 指定一个图片来源(本地图片资源,或线上图片资源):

<!-- 引用本地图片(相对路径) -->
<img src="taobao.png" />

<!-- 引用线上图片(绝对路径) -->
<img src="https://picsum.photos/400?random=4" />

或者说,把图片在CSS中当作背景被用于 Web 中:

<!-- HTML -->
<div class="container"></div>

<style>
    .container {
        background-image: url("https://picsum.photos/400?random=4"); /* url() 可以引用本地和线上的图片资源 */
    }
</style>  

事实上,要在 Web 上用好图片并不是件易事,大部分 Web 开发者在使用图片时往往选择性的忽略很多必做的事情,比如:

  • 确保在 <img>alt 添加描述图片的信息
  • 如果你想用可见的文本来描述图片信息,应该使用 <figure> 标签来包裹 <img> ,并使用 <figcaption> 给图片提供显示描述文本信息
  • <picture> 元素中加入 <source> 元素,引入不同格式的图片源
  • 为不同的屏幕尺寸和其他条件提供不同的图片源
  • 对图片的格式有足够的了解,可以在适当的时候使用 SVG 等格式
  • 对你的受众有足够的了解,可以默认使用现代的格式
  • 使用 srcsetsizes 属性来提供不同大小的图片,以便在不同的设备上节省带宽(为了尽可能的高效,创建这些图片变化取决于图片本身,而不是预先确定的尺寸)
  • 优化图片和它的所有变化。尝试使用不同的优化软件,挑选结果最佳的。确保优化后的版本不会意外的比原来尺寸大或质量低
  • 懒加载图片,这样用户就不必下载他们不会看到的图片。HTML 的 loading="lazy" 可以实现图片的懒加载
  • 确保在 <img> 上显式设置 widthheight 属性的值,以便图片在加载前在布局中保留正确的空间(即使布局是自适应的)
  • 将你的图片托管在一个快速的、无 cookie的全球 CDN上。可以是 <link rel="preconnect"><link rel="dns-prefetch"> 到 CDN 域。但不要在开发阶段做任何 CDN 的事情,只在生产阶段做。你的图片的标准来源应该是你自己的服务器,所以你可以根据需要移动 CDN
  • 页面上最大的图片可以提前预加载,比如 <link rel="preload" as="image"> 以获得最佳的 LCP
  • 试着弄清楚什么时候应该使用 decoding="async" (图片解码方式)
  • 在图片没有加载的情况下,要有降级样式
  • 考虑更多令人愉悦的加载前或加载失败的图片,比如小的模糊版本的图片
  • 要有性能监控,以确保大图片不会被使用

可能你会说,无法做到上面列出的(或未列出又对图片有利的优化)。不过现实是,有时候只做其中的一些事情,哪怕得到微小的变化,对于用户来说都是有益的。在社区中,建立在 <img> 基础上的图片组件,比如 Next.js<Image>(用于React环境)和 Nuxt<Image> (用于 Vue环境)都尽量在默认情况下加入这些概念。另外 Eleventy<Image> 组件和 gatsby-plugin-image 都在如何将这些东西自动化并尽可能多地提供这些最佳实践方面受到好评。

图片是如何影响用户体验和 CWV 的核心指标

你可能已经听说过 核心Web指标(CWV,Core Web Vitals)。CWV 是 Google 发布的一套新的用来衡量页面体验的最终指标。简单地说,CWV 是模拟真实的场景,以用户为中心的一系列用户体验指标。主要衡量Web页面可用性的多个维度,分别是 加载时间(Loading) 、交互性(Interactivity) 和 内容加载时的稳定性(Visual Stability)

这三个指标分别是 LCP (对应 Loading)、FID (对应 Interactivity) 和 CLS (对应 Visual Stability)。

上图中,每个指标都有三个等级的划分,绿色部分表示优秀、黄色部分表示一般(略及格),红色部分表示极差。如果你的 Web 页面这三项指标都达到绿色级别,意味着你给用户提供了良好的用户体验。而 Web 中的图片却可以通过多种方式影响 CWV,让这几项指标达不到好的分数

(上图:聚划算首页,氛围图直接拉低了 LCP 的指标,LCP测量用户视窗中最大的内容元素,比如图片、文本。氛围图何时变得可见,直接决定 LCP 是否达标)

在许多现代 Web 体验中,当一个页面完成加载时,图片往往是最大的可见元素。比如氛围图、英雄图(横幅,即 Hero Image)、旋转木马的图片(轮播图片,即 Carousel)、故事图(Stories)和 广告图(Banner)。LCP(Largest Contentful Paint)是 CWV 的重要指标之一,用于衡量用户视窗中最大的内容元素何时可见。例如这里提到的这些图片之一。

注意,LCP 会检测图片(<img><picture>)、SVG矢量图(<svg>)、视频(预览图)、通过 url() 引入的图片资源和包含文字的块元素或行内元素。

这使得浏览器能够确定页面的主要内容何时完成渲染。当图片是最大的内容元素时,该图片的加载速度会影响 LCP。除了应用图片压缩(比如 SquooshSharpImageOptim图片 CDN),并使用现代图片格式外,你可以调整 <img> 元素,以提供最合适的图片响应式版本或图片懒加载。

(上图是 WebPageTest filmstrip中显示的 Largest Contentful Paint,即LCP)

布局的变化(Layout Shifts)会让用户分心。想象一下,当你开始浏览Web页面时,突然间页面上的元素发生了变化,你不得不重新找到变化前浏览的位置。是不是会有一种不适(不舒服)的感觉(体验)。CWV中的 CLS(Cumulative Layout Shift)指标就是用来衡量内容的稳定性,即 衡量用户在整个页面生命周期中发生的布局偏移情况

导致 CLS 的最常见的原因包括没有尺寸的图片,即 <img> 没有显式设置 widthheight 属性的值,这些图片在加载时可能会让其他元素内容移动,比如内容往下移。也就是说,忽略 <img>widthheight 设置意味着浏览器可能无法知道它们加载前保留足够的空间。

(上图是 聚划算移动端CLS)

(上图是聚划算桌面端 CLS)

CLS的动图可以通过 Cumulative Layout Shift Debugger(CLS)Layout Shift Gif Generator来录制。

图片有可能在页面加载时阻塞用户的带宽和 CPU。它们会妨碍关键资源加载,特别是在较慢的网格连接和低端移动设备上,导致带宽饱和。CWV的首次输入延迟 FID (First Input Delay)指标,可以捕捉到用户对 Web 页面的互动性和响应性的第一印象。即 从用户第一次与页面交互(如点击链接、点击按钮或使用自定义的 由 JavaScript 脚本自驱动的控件)到浏览器实际能够开始处理事件的时间间隔。FID 主要作用就是用来衡量页面的交互的性能体验。

换句说,FID衡量的是,当用户认为页面已经完成时,它是否真的已经完成。如果用户点击页面时,浏览器正忙于下载、解析和运行 JavaScript 脚本,那么在浏览器处理事件和触发点击事件之前,会有一个延迟。 FID 测量这种延迟。

比如下面这个示例:

通常情况下,页面加载时会发生多个请求去加载资源(比如 CSS、JavaScript 、字体和图片等),上图中黄色代表此时主线程正在忙碌中。

这个时候其实已经有部分内容加载出来了(FCP),但是距离页面真正可交互时间(TTI:Time to Interactive)的节点还有一段时间。

而此时,如果用户进行了点击或输入操作,主线程实际上会将该任务挂起,在部分资源加载执行完成后才执行该任务。因此,这时候就有了 FID 耗时。

上图中,FID被捕获并显示在控制台中。该页面有一些缓慢的 JavaScript 脚本,在页面加载时阻塞了浏览器的主线程。即 缓慢的 JavaScript 延迟了用户的第一次点击(图中黄色点)。

为此,我们可以通过减少主线程的 CPU 使用率来减少 FID。

Lighthouse

我们可以使用 Lighthouse来确定改进 CWV 的方案。 Lighthouse 是一个开源的自动化工具,用于提高网页的质量。你可以在 Chrome DevTools 调试工具套件中找到它,并针对任何网页运行它。你还可以在 PageSpeed InsightsCIGTmettrixWebpageTest中找到 Lighthouse。

Lighthouse 是一个实验工具。虽然它很适合用来寻找改善用户体验存在的问题(或缺陷),但还是要尽量参考真实环境中的数据,才能更全面了解实际用户的情况。

基础知识

要在一个 Web 页面上放置一张图片,使用 HTML 的 img 标签元素即可。<img> 标签至少需要一个 src 属性,引用图片源:

<!-- 引用本地图片(相对路径) -->
<img src="taobao.png" />

<!-- 引用线上图片(绝对路径) -->
<img src="https://picsum.photos/400?random=4" />

为了确保我们的图片具有可读性,需要在 <img>alt 属性上添加图片的文本描述,当图片无法显示或看到时,它被用作图片的替代品;或者像屏幕阅读器这样的 ATs 访问你的图片时,它就会朗读出 <img>alt 中设置的文本描述(如果未设置,会朗读图片的文件名)。在 <img> 中指定 alt 属性的值,看起来像下面这样:

<!-- 引用本地图片(相对路径) -->
<img src="taobao.png" alt="淘宝,太好逛了吧!" />

如果你想更深入的探究为什么要给 <img> 设置 alt 属性值以及如何更好的设置 alt 值感兴趣的话,可以阅读下面这些教程:

接下来,我们在 <img> 显式设置 widthheight 属性的值来指定图片的宽度和高度(图片的尺寸):

<img src="juhuasuan.jpg"
    alt="聚划算"
    width="117"
    height="30" >

当在图片上指定了 widthheight 时,浏览器就知道要为该图片保留多少空间,直到它被下载。如果没有设置图片的尺寸,就会导致布局变化(CLS),因为浏览器不确定图片需要多少空间。

现代浏览器可以根据图片的 widthheight 属性来设置图片的默认宽高比(Aspect Ratio),所以设置它们来防止布局移动(CLS)是有很价值的。

确定 LCP 的元素

影响 LCP 的元素类型主要有:

  • <img> 元素
  • 内嵌在 <svg> 元素内的 <image> 元素
  • <video> 元素 (使用封面图片(poster 引入的图片)测量 LCP)
  • 通过 url() 函数(而非 CSS 渐变)加载的带有背景图片的元素
  • 包含文本节点或其他行内级文本元素的 块级 元素

Lighthouse 有一个 “LCP” 的审查,可以帮助我们识别页面上哪个元素是 LCP 元素。将鼠标悬停在该元素上,会在主浏览器窗口中突出显示该元素:

如果这个元素是一个 <img> ,这个信息是一个有用的提示,你可以对该图片进行优化(比如优化和压缩图片)。你也可以直接在 Lighthouse 的 LCP 审查区域点击该元素,会直接跳到开发者工具的 元素(Elements) 面板,定位到该元素在 DOM 中的位置:

如果在首屏渲染,加载这些元素(影响 LCP 的元素类型)所需的时间将对 LCP 产生直接影响。有几种方法可以确保尽快加载这些文件:

  • 优化和压缩图片
  • 预加载重要资源
  • 基于网格连接交付不同资源(自适应服务)
  • 使用 Service Worker 缓存资产

正如聚划算首页(其实大多网站来说),在页面加载完毕后,图片会是视图中的最大元素。比如页面的氛围图、横幅广告图和大型轮播图等。改善这些类型的图片加载和渲染所需的时间将直接提升 LCP 的速度。实现方式:

  • 首先考虑不使用图片。如果图片与内容无关,请将其删除
  • 压缩图片
  • 将图片转换为更新的格式,比如 JPEG 2000、JPEG XR 或 WebP 等
  • 使用响应式图片
  • 考虑使用图片 CDN

其实在 Lighthouse 提供的检测报告也会提供针对 LCP 优化的一些建议:

不过我们这篇文章仅关注的是与图片相关的优化。比如聚划算氛围图:

就可以在 <link> 标签使用 preload 对图片进行预加载:

<link rel="preload" as="image" href="wolf.png" />

如果 <img> 中使用了 srcsetsizes 相关特性(实现响应式图片),也可以像下面这样对图片进行预加载:

<link
    rel="preload"
    as="image"
    href="wolf.jpg"
    imagesrcset="wolf_400px.jpg 400w, wolf_800px.jpg 800w, wolf_1600px.jpg 1600w"
    imagesizes="50vw"
    />

识别来自没有尺寸的图片引起的 CLS

就浏览器渲染图片来说,如果图片没有尺寸(<img> 未显式设置 widthheight),在图片加载完成之前,浏览器是不知道要预留多少空间给图片的。如此一来就会引起累积布局偏移(CLS)。

其实不仅图片元素未设置 widthheight 会引起 CLS,在 We

剩余80%内容付费后可查看

如需转载,烦请注明出处:https://www.w3cplus.com/performance/web-performance-for-image.html

如果文章中有不对之处,烦请各位大神拍正。如果你觉得这篇文章对你有所帮助,打个赏,让我有更大的动力去创作。(^_^)。看完了?还不过瘾?点击向作者提问!

赏杯咖啡,鼓励他创作更多优质内容!
返回顶部