描述性语法

在本单元中,您将学习如何允许浏览器选择图片,以便浏览器做出最佳的显示内容决策。srcset 不是在特定断点处更换图片来源的方法,也不意味着在一张图片上更换另一张图片。这些语法允许 来解决一个独立于我们之外的非常棘手的问题:无缝请求和呈现根据用户浏览背景定制的图片源; 包括视口大小、显示密度、用户偏好设置、带宽以及众多其他因素。

这是个大问题 - 当我们单纯地为网络标记图片时,肯定是需要考虑的,而且做得好需要更多 信息量过大

使用 x 描述密度

无论用户的密度如何,具有固定宽度的 <img> 在任何浏览上下文中都将占据相同大小的视口。 display—构成其屏幕的物理像素数量。例如,固有宽度为 400px 的图片几乎会占据 原始 Google Pixel 和新款 Pixel 6 Pro 上的整个浏览器视口都存在,这两款设备都具有标准化 412px 逻辑像素宽视口。

Pixel 6 Pro 的屏幕更清晰,但是 Pixel 6 Pro 的物理分辨率为 1440 × 3120 像素,而 Pixel 为 1080 × 1920 像素,即构成屏幕本身的硬件像素数量。

设备的逻辑像素与物理像素之间的比率是指相应显示屏的设备像素比 (DPR)。DPR 为 用设备的实际屏幕分辨率除以视口的 CSS 像素计算得出。

控制台窗口中显示的 DPR 为 2。

因此,原始 Pixel 的 DPR 为 2.6,而 Pixel 6 Pro 的 DPR 为 3.5。

iPhone 4 是首款 DPR 大于 1 的设备,报告的设备像素比为 2,屏幕的物理分辨率为 双倍逻辑分辨率。iPhone 4 之前的所有设备的 DPR 为 1:一个逻辑像素与一个物理像素。

如果您在 DPR 为 2 的显示屏上查看宽度为 400px 的图像,则会在 DPR 为 2 的显示屏上 显示屏的物理像素:两个水平和两个垂直。该图片对高密度显示屏没有好处,因为它看起来像是 与在 DPR 为 1 的显示屏上效果相同。当然,任何“绘制”的由浏览器的渲染引擎(文本、CSS 形状或 SVG)决定的, 例如,系统将进行绘制以适合密度较高的显示屏。不过,正如您从图片格式和压缩中了解到的,光栅图片是固定的 像素网格。虽然光栅图像可能并不总是很明显,但放大后的光栅图像可以适应密度较高的显示屏。 与周围的网页相比,分辨率较低。

为防止放大,所渲染图片的固有宽度必须至少为 800 像素。缩减时 以便在宽度为 400 逻辑像素的布局中适应空间,800 像素的图片来源的像素密度是 DPR 为 2 的两倍, 就会美观又清晰

显示密度差异的花瓣的特写。

由于 DPR 为 1 的显示屏无法使用更高的图像密度,因此系统会缩小图像以匹配 而如您所知,缩小过版的图像还是没问题的。在低密度显示屏上,适合显示适合较高密度的图片 与其他任何低密度图片的外观一样。

正如您在图片和性能一文中所述,一位使用低密度显示屏的用户查看的是缩小到 400px 的图片来源 将仅需要固有宽度为 400px 的来源。一张更大的图片可以很好地满足所有用户的视觉需求, 在低密度的小显示屏上呈现的高分辨率图片来源,其显示效果与其他任何小型低密度图片一样,但显示速度要慢得多。

您可能已经猜到,DPR 为 1 的移动设备极为罕见, 但仍在“桌面设备”中很常见浏览上下文。根据数据 Matt Hobbs 分享的数据显示,2022 年 11 月以来,在 GOV.UK 浏览会话中,约 18% 的人报告 DPR 为 1。虽然高密度图片的外观如同这些用户所期望的那样,但是它们的带宽和处理成本要高得多, 对于那些仍可能配备低密度显示屏的旧款和低密度设备的用户而言,值得特别关注。

使用 srcset 可确保只有配备高分辨率显示屏的设备才能接收足够大且清晰可辨的图片源,而不会达到要求的图片源 显示屏分辨率较低的用户所付出的带宽成本。

srcset 属性用于标识一个或多个用于渲染图片的候选图片(以英文逗号分隔)。每个候选定位设置 两部分内容:一个网址(类似于您在 src 中使用),一个用于描述该图片来源的语法。srcset的每位候选人 由自身的宽度(“w 语法”)或目标密度(“x 语法”)描述。

x 语法是“此来源适用于具有此密度的显示屏”的简写形式 - 后跟 2x 的候选字符是 适用于 DPR 为 2 的显示屏

<img src="low-density.jpg" srcset="double-density.jpg 2x" alt="...">

系统会为支持 srcset 的浏览器显示两个候选键:double-density.jpg2x 会酌情对其进行描述 (适用于 DPR 为 2 且 src 属性为 low-density.jpg)的显示屏,如果系统没有更合适的选项,则选择相应的候选网络 在“srcset”中找到。对于不支持 srcset 的浏览器,该属性及其内容将被忽略,即 src 的内容 会像往常一样被请求

我们很容易将 srcset 属性中指定的值误认为是指令。2x 会告知浏览器 相关联的源文件适合在 DPR 为 2(关于来源本身的信息)的显示屏上使用。它不会说明 浏览器如何使用该来源,只是告知浏览器可如何使用该来源。这两者之间有一种微妙而重要的区别: 是双密度图片,而不是可在双密度显示屏上使用的图片。

表示“此来源适用于 2x 显示屏”的语法之间的区别以及一条内容为“在 2x 上使用此来源”的屏幕 在印刷文字上看似微不足道,但显示密度只是浏览器在决定候选定位设置的众多相互关联的因素之一 但只有其中一部分是您能知道的。例如:您可以单独确定用户启用了 通过 prefers-reduced-data 媒体查询以节省带宽的浏览器偏好设置,并使用该查询始终允许用户观看低密度图片 无论其显示密度如何,但除非每个开发者在每个网站上以一致的方式实现此技术,否则它对用户并没有太大的用处。 他们可能在一个网站上照顾了他们的偏好,却在另一个网站上遇到图片带宽无法消除的图片墙。

srcset/sizes 所用的故意含糊不清的资源选择算法给浏览器留出了选择较低密度的空间 出现带宽低谷的情况,或为了尽量减少流量消耗而优先使用的图片,则 Google 无需承担使用方式、时间或 阈值如果浏览器有能力为您处理更多责任和额外的工作,那就没必要去做了。

使用 w 描述宽度

srcset 接受用于图片来源候选项的另一种类型的描述符。它的作用要强大得多,对我们来说, 更容易理解您无需将候选广告标记为具有适合指定显示密度的尺寸, w 语法描述了每个候选来源的固有宽度。同样,除了维度方面相同,每个候选定位设置都相同 相同的剪裁范围和宽高比。但在这种情况下,您需要用户浏览器在两个候选字词之间进行选择: small.jpg(固有宽度为 600 像素的来源)和 large.jpg(固有宽度为 1200 像素的来源)。

srcset="small.jpg 600w, large.jpg 1200w"

这不会告知浏览器应如何处理此信息,而只会为其提供用于显示图片的候选对象列表。 在浏览器确定要呈现哪个来源之前,您需要为其提供更多信息: 描述图片在网页上的呈现方式。为此,请使用 sizes 属性。

使用 sizes 描述使用情况

在传输图片方面,浏览器的性能令人惊叹。发起图片素材资源请求的时间过长 在请求样式表或 JavaScript 之前(通常甚至在标记完全解析之前进行)。当浏览器 应用发出这些请求后,除了标记之外,并无与网页本身相关的信息 — 它甚至可能没有发起请求 外部样式表的样式,更不用说应用了。当浏览器解析您的标记并开始将外部链接设置为外部时 请求,它只包含浏览器一级的信息:用户视口的尺寸、用户显示屏的像素密度 用户偏好设置等等

这并不能说明图片在页面布局中的预期呈现效果,它甚至无法使用视口。 img 大小上限的代理,因为它可能会占用水平滚动容器。因此,我们需要 为浏览器提供这些信息,并使用标记来实现。以上就是我们针对这些请求所能使用的全部信息。

srcset 一样,sizes 用于在解析标记后立即提供图片的相关信息。与 srcset 一样 属性是“此处是源文件及其固有大小”的简写,sizes 属性是“此处”的简写形式 是布局中渲染图像的大小。描述图片的方式取决于视口(同样是视口) size 是发出图片请求时浏览器具有的唯一布局信息。

这在平面上听起来可能有点复杂,但在实践中理解要简单得多:

<img
 sizes="80vw"
 srcset="small.jpg 600w, medium.jpg 1200w, large.jpg 2000w"
 src="fallback.jpg"
 alt="...">

在此示例中,这个 sizes 值会告知浏览器 img 所占的布局空间的宽度为 80vw—80% 视口请注意,这不是说明,而是对图片在页面布局中尺寸的说明。这并不是说“让这个 占 80% 的视口”而是“在网页呈现后,该图片最终将占据 80% 的视口”

作为开发者,您的工作就此结束。您已在 srcset中准确描述了候选来源列表以及图片的宽度 与 srcset 中的 x 语法一样,其余内容由浏览器决定。sizes

但为了充分理解这些信息的使用方式,让我们花点时间了解一下 用户的浏览器在遇到此标记时创建的:

您已告知浏览器此图像将占据 80% 的可用视口,因此,如果我们在img 视口宽度为 1000 像素的设备上,该图片将占据 800 像素。然后,浏览器将用该值除以 设置为我们在 srcset 中指定的每个候选图像来源的宽度。最小的源尺寸为 600 像素 求:600÷800=0.75。我们的中等图片宽度为 1200 像素:1200÷800=1.5。我们最大的图片宽度为 2000 像素:2000÷800=2.5。

实际上,这些计算(.751.52.5)的结果就是专门针对用户的 DPR 选项 视口大小。由于浏览器还有关于用户当前显示密度的信息,因此会做出一系列决定:

在此视口大小下,无论用户的显示密度如何,候选 small.jpg 都会被舍弃,并且计算出的 DPR 较低 高于 1,则对于任何用户,该来源都要求进行升频,因此并不适合。在 DPR 为 1 的设备上,medium.jpg 提供 最接近的匹配 — 该来源适合以 1.5 的 DPR 显示,因此会略大一些,但请记住,缩减 一个视觉效果无缝衔接的过程。在 DPR 为 2 的设备上,large.jpg 是最接近的匹配项,因此会被选中。

如果在宽度为 600 像素的视口上渲染了同一图像,那么计算结果将完全不同:80vw 现在是 480px。 当我们划分来源的然后得出 1.252.54.1666666667。在此视口大小下,将选择 small.jpg 在 1 倍的设备上,medium.jpg 将在 2 倍的设备上匹配。

此图片在所有这些浏览环境中看起来都一样:除了尺寸之外,我们所有的源文件都完全相同, 每个屏幕都渲染得像用户的显示密度所允许的范围一样清晰。不过,我们不为每个用户提供 large.jpg 为了适应最大的视口和最高密度的显示,系统始终会向用户投放适合的最小候选广告。 通过使用描述性语法而不是规定性语法,您无需手动设置断点,也无需考虑未来的视口和 DPR - 您只需为浏览器提供信息,并让它为您确定答案。

由于 sizes 值是相对于视口而言的,并且完全独立于页面布局,因此会增加一个复杂功能层。 占据视口一定百分比,没有任何固定宽度的外边距、内边距或影响非常少见 与网页上的其他元素区分开来您经常需要使用单位组合来表示图像的宽度;百分比、empx 等。

幸运的是,您可以在此使用 calc(),任何原生支持自适应图片的浏览器也将支持 calc(),这使得我们能够 混搭 CSS 单元 - 例如,占据用户视口整个宽度,但两侧均减去 1em 外边距的图片:

<img
    sizes="calc(100vw-2em)"
    srcset="small.jpg 400w, medium.jpg 800w, large.jpg 1600w, x-large.jpg 2400w"
    src="fallback.jpg"
    alt="...">

描述断点

如果您在编写响应式布局方面花了很多时间,很可能会发现这些示例缺少一些内容: 图片在布局中所占空间很可能在布局的断点处发生变化。在这种情况下 将更多详细信息传递给浏览器:sizes 接受以逗号分隔的一组候选项,这些候选项的渲染大小 就像 srcset 一样,接受以英文逗号分隔的图片来源候选名称。这些条件使用的是我们熟悉的媒体查询语法。 此语法是首次匹配:一旦媒体条件匹配,浏览器就会停止解析 sizes 属性,值 。

假设某张图片要占据 80% 的视口,并在两边各减去一个 em 的内边距,即在高于 1200px 的视口上, 较小视口,则会占据视口的全宽。

  <img
     sizes="(min-width: 1200px) calc(80vw - 2em), 100vw"
     srcset="small.jpg 600w, medium.jpg 1200w, large.jpg 2000w"
     src="fallback.jpg"
     alt="...">

如果用户的视口大于 1200px,calc(80vw - 2em) 会描述布局中图片的宽度。如果 (min-width: 1200px) 条件不匹配,则浏览器会移至下一个值。由于没有 媒体条件,100vw 为默认值。如果您要在编写此 sizes 属性时使用 max-width 个媒体查询:

  <img
     sizes="(max-width: 1200px) 100vw, calc(80vw - 2em)"
     srcset="small.jpg 600w, medium.jpg 1200w, large.jpg 2000w"
     src="fallback.jpg"
     alt="...">

用浅显的话说:“(max-width: 1200px)匹配吗?如果不是,请继续。下一个值 calc(80vw - 2em) 没有限定条件, 这就是所选项

现在,您已经向浏览器提供了有关 img 元素的所有此类信息,包括潜在来源、固有宽度、 以及您打算如何向用户展示图片,那么浏览器会使用一组模糊规则来确定 这些信息。如果您听起来含糊不清,这是因为设计上确实如此。以 HTML 规范明确未明确应如何选择来源。找到来源、其描述符,以及 将要呈现的图片都已解析完毕,浏览器可以随意执行所需的任何操作,您无从确定 浏览器选择的任何源代码。

显示“在高分辨率显示屏上使用此来源”的语法是可以预测的,但无法解决核心问题 采用响应式布局的图片:节省用户带宽。屏幕的像素密度与互联网只是切线关系 如果您使用的是顶级笔记本电脑,但通过按流量计费的连接浏览网页,则使用网络共享功能 连接到手机,或是在使用信号不佳的飞机 Wi-Fi 连接时,您可能会希望停用高分辨率图片来源, 显示质量。

将最终决定权留给浏览器带来的性能提升远远超过严格规范所能达到的性能提升 语法。例如:在大多数浏览器中,使用 srcsetsizes 语法的 img 永远不会请求使用较小 尺寸大于用户浏览器缓存中已有的尺寸。针对来源提出新的请求有什么意义 那么当浏览器可以无缝地缩小其已有的图片来源时呢?但是,如果用户将其规模 视口直至需要新图片以避免放大时,系统仍会发出该请求,因此, 显示效果与您的预期一致。

缺少明确控制听起来有点可怕,但由于你使用的源文件具有完全相同的 我们不会再向用户展示“已损坏”的与使用单源 src 相比,无论如何 浏览器作出的决策

使用 sizessrcset

无论是对您自己还是对读者来说,对于浏览器来说,这都是大量的信息。srcsetsizes 都是密集语法, 用相对较少的字符来描述大量信息。也就是说,无论好坏,设计上就是: 这些语法虽然不太简洁,并且更易于人类解析,但可能会让浏览器更加难以解析。通过 字符串越复杂,发生解析器错误或行为上的意外差异的可能性就越大 从一个浏览器转移到另一个浏览器。不过,这也有一个缺点:机器更容易阅读的语法是更容易编写的语法 错误。

srcset 非常适合自动化。您很少需要为某个客户手动制作 而不是使用 Gulp 等任务运行程序、Webpack 等第三方打包器 Cloudinary 等 CDN,或是所选 CMS 中已有的功能。获得足够的信息来生成信息来源 首先,系统有足够的信息来将它们写入有效的 srcset 属性。

自动化 sizes 稍微有点难。如您所知,系统计算图片大小的唯一方式 渲染布局就是渲染布局。幸运的是,很多开发者工具已涌现 手写 sizes 属性的过程 - 这种效率是无法通过手写完成的。 例如,respImageLint 是用于审查 sizes 属性的代码段 以确保准确性并提供改进建议。Lazysizes 项目遭遇入侵 将图片请求延迟到布局创建之后再执行,从而加快效率,从而允许 JavaScript 生成 sizes 值。如果您使用的是完全客户端渲染框架(例如 React 或 Vue), 提供了许多用于编写和/或生成 srcsetsizes 属性的解决方案,我们将在 CMS 和框架中进一步讨论这些解决方案。