关键性能问题

就目前而言,无论是从总传输大小还是从每页请求数量来看,图片都是 Web 上最大的资源。截至 2022 年 6 月,网页的中位总传输大小约为 2MB,其中图片几乎占一半。毫不夸张地说,优化图片请求可能是您可以做的最重要的性能优化。

稍后,您将了解自适应图片是如何演变以帮助解决尝试为所有可能性提供一张图片而产生的问题。在本节中,您将了解与图片相关的关键性能指标,以及如何改进它们。

延迟图片请求

虽然您将要学习许多方法来确保您的图片请求尽可能小且高效,但最快的图片请求始终是永远不会发出的请求。因此,首先,我想分享您可以在图片资源交付给用户的方式上做出的可能最具影响力的更改:loading="lazy" 属性。

<img src="image.jpg" loading="lazy" alt="…">

此属性确保在图片靠近用户视口时才发出图片请求,从而将它们从初始页面加载(浏览器最繁忙的时候)中延迟,并将这些请求从关键渲染路径中删除。

实际上,使用此属性可能会对性能产生巨大的积极影响:永远不会进入用户视口的图片将永远不会被请求,并且不会浪费带宽在用户永远不会看到的图片上。

但是,有一个问题:延迟这些请求意味着无法利用浏览器高度优化的流程来尽可能早地请求图片。如果在布局顶部的 img 元素上使用 loading="lazy"(因此更有可能在首次加载页面时位于用户视口中),则最终用户可能会感觉这些图片加载速度明显变慢。

Fetch Priority

loading 属性是更大的 Web 标准工作的一个示例,旨在让开发者对 Web 浏览器如何确定请求优先级拥有更多控制权。

您可能知道浏览器确定获取优先级的基本方法:例如,对于文档 <head> 中的外部 CSS 文件的请求被认为足够重要,会阻止渲染,而对于 </body> 上方的外部 JavaScript 文件的请求将被延迟到渲染完成之后。如果 <img> 上的 loading 属性值为“lazy”,则关联的图片请求将延迟到浏览器确定它将显示给用户时。否则,该图片的优先级将与页面上的任何其他图片相同。

fetchpriority 属性旨在让开发者更精细地控制资源的优先级,允许您将资源标记为相对于同类型资源的“高”优先级和“低”优先级。fetchpriority 的用例与 loading 属性类似,但范围更广。例如,您可以在仅在用户交互后才显示的图片上使用 fetchpriority="low"(无论该图片是否在用户视口中),以便优先处理页面其他位置的可见图片,或者使用 fetchpriority="high" 来优先处理您知道在页面渲染后会立即在视口中可见的图片。

请注意,fetchpriorityloading 的不同之处在于,它不会从根本上改变浏览器行为。它不会指示浏览器先加载某些资源,而是为其围绕请求资源所做的决策提供重要的上下文。

衡量图片的影响

在优化图片资源时,感知到的性能通常比总传输大小本身更重要,也更难以衡量。

Web Vitals 提供可衡量、可操作的指标和指南,用于改善用户对 Web 的体验,突出显示诸如 Web 服务器响应时间慢、渲染问题和交互延迟等问题。Core Web Vitals 是这些目标的一个子集,专注于用户对单个页面的直接体验——一组技术测量指标,它们共同决定了用户感觉体验有多快。

累积布局偏移

累积布局偏移 (CLS) 是衡量视觉稳定性的指标。它是衡量页面上内容的布局在资源加载和页面渲染时偏移程度的指标。任何在 Web 上花费大量时间的人都可能会因为延迟的 Web 字体或图片源突然渲染而导致页面“跳动”而迷失在长篇文本中,或者交互式元素突然从您的指针移开。高 CLS 最坏的情况是引起用户错误——例如,就在用户单击时,“取消”按钮移动到先前由“确认”按钮占据的空间。

考虑到高加载时间和它们在布局中可能占据的空间量,图片是导致高 CLS 分数的常见原因,这很合理。

得益于现代浏览器中相对较新的更改,避免因图片导致高 CLS 分数比您想象的要容易。

如果您从事前端工作已有几年了,您会熟悉 <img> 上的 widthheight 属性:在 CSS 广泛采用之前,这些是控制图片大小的唯一方法。

<img src="image.jpg" height="200" width="400" alt="…">

这些属性已经不再使用,目的是将我们的样式关注点与标记分开,尤其是在自适应 Web 设计使通过 CSS 指定基于百分比的大小成为必要之后。在自适应 Web 设计的早期,“删除未使用的 widthheight 属性”是一个常见的建议,因为我们在 CSS 中指定的值——max-width: 100%height: auto——会覆盖它们。

<img src="image.jpg" alt="…">
img {
  max-width: 100%;
  height: auto;
}

在删除了 heightwidth 属性(如上例所示)之后,浏览器在这种情况下确定图片高度的唯一方法是请求源,解析它,并根据样式表应用后它在布局中占据的空间宽度,以其固有纵横比渲染它。此过程的大部分发生在页面渲染之后,新计算的高度会导致额外的布局偏移。

从 2019 年开始,浏览器行为已更新为以不同的方式处理 widthheight 属性。这些属性的值不再用于确定布局中 img 元素的固定像素大小,这些属性可以被认为代表图片的纵横比,尽管语法相同。现代浏览器将把这些值相互除以,以便在页面渲染之前确定 img 元素的固有纵横比,从而允许它在渲染布局时保留图片将要占据的空间。

作为规则,您应该始终在 <img> 上使用 heightwidth 属性,其值与图片源的固有大小相匹配——只要您确保在 max-width: 100% 的同时指定了 height: auto,以覆盖 HTML 属性中的高度。

<img src="image.jpg" height="200" width="400" alt="…">
img {
  max-width: 100%;
  height: auto;
}

通过在 <img> 元素上使用 widthheight 属性,您将避免因图片导致高 CLS 分数。

重要的是要注意,这种方法没有任何缺点,因为它依赖于长期建立的浏览器行为——任何支持基本 CSS 的浏览器都将像往常一样工作,您的标记中的 heightwidth 属性将被您的样式覆盖。

虽然 widthheight 属性通过为图片保留必要的布局空间来巧妙地避免了 CLS 问题,但在用户等待图片传输和渲染时,向用户呈现空白间隙或低质量占位符也不是理想的。虽然您可以采取一些措施来减轻加载缓慢的图片的可衡量和可感知的影响,但更快地向用户呈现完全渲染的图片的唯一方法是减小其传输大小。

最大内容渲染

最大内容渲染 (LCP) 衡量渲染用户视口中可见的最大“内容”元素所需的时间——即占据可见页面百分比最大的内容元素。从表面上看,这似乎是一个过于具体的指标,但该元素可以作为页面大部分内容已从用户角度渲染完毕的实际代理。LCP 是衡量(感知到的)性能的重要指标。

诸如 DOMContentLoadedwindow.onload 事件之类的指标可用于确定何时从技术上完成加载当前页面的过程,但它们不一定对应于用户的页面体验。渲染用户视口外部的元素时的轻微延迟将被计入这些指标中的任何一个,但很可能完全不会被真实世界的用户检测到。较长的 LCP 意味着用户对页面的第一印象——当前视口中最重要内容——是页面速度慢或完全损坏。

沃达丰所做的一项实验发现,LCP 提高了 31% 不仅带来了 8% 的销售额增长(本身就是一个强劲的结果),而且在其总用户数中,访问者转化为潜在客户的比率提高了 15%(“访问者到潜在客户转化率”),访问其购物车的用户数量提高了 11%(“购物车访问率”)。

在超过 70% 的网页上,初始视口中最大的元素都包含图片,无论是作为独立的 <img> 元素还是带有背景图片的元素。换句话说,70% 的页面的 LCP 分数都基于图片性能。不难想象为什么:大的、引人注目的图片和徽标很可能“位于首屏”。

LCP highlighted in the console of a web.dev page

您可以采取一些步骤来避免 LCP 延迟:首先,永远不要在“首屏”图片上指定 loading="lazy",因为将请求延迟到页面渲染之后可能会对您的 LCP 分数产生巨大的负面影响。其次,使用 fetchpriority="high" 可以告知浏览器应优先传输此图片,而不是页面上其他位置的图片。

牢记这些规则,您可以为提高页面的 LCP 分数做的最重要的事情是减少传输和渲染这些图片所需的时间。为此,您需要尽可能小巧高效地保持图片源(当然,前提是不牺牲其质量),并确保用户仅获得对其浏览上下文最有意义的图片资源。

总结

图片资源是对用户带宽的最大消耗——带宽从传输渲染页面所需的每个其他资源中夺走。图片在感知性能方面引入了重大问题,无论是在周围页面布局渲染期间还是渲染之后。简而言之:图片资源会造成损害

尽管这可能令人望而生畏,但虽然“Web 如果图片更少会更好”在仅考虑性能方面肯定是正确的,但也会对用户造成巨大的损害。图片是 Web 的重要组成部分,为了性能而牺牲有意义内容的质量是不应该的。

在本课程的其余部分中,您将了解为我们的图片资源提供支持的技术以及减轻其性能影响的技术,而不会在质量上做出妥协。