2025 年你可能真的不知道如何用好标签?

内容分享4周前发布
0 1 0

家好,很高兴又见面了,我是”高级前端‬进阶‬”,由我带着大家一起关注前端前沿、深入前端底层技术,大家一起进步,也欢迎大家关注、点赞、收藏、转发,您的支持是我不断创作的动力。

<img src="https://pic.songma.com/blogimg/20251118/154d7dfcc4f54be28bb63e8f098684c0.jpg" alt="2025 年你可能真的不知道如何用好标签?”>标签?” title=”2025 年你可能真的不知道如何用好标签?”>

根据 HTTPArchive 数据,至少 70% 的网站图片位置超级突出,但只有 34% 的网站使用 <img srcset> 来创建响应式高性能图片,而使用 <picture> 的网站则更少。

本文将围绕响应式图片技术,介绍一种旨在使 <img> 标签像 <picture> 一样工作的新技术,从而更容易在现有代码库中进行大批量的迁移。

1. 为什么需要响应式图片 (Responsive Images)

一般来说,高质量的图片会带来更好的用户体验。

大屏幕和小屏幕,例如:桌面和移动设备,应该能显示不同的图片尺寸。手机的屏幕空间较小,因此需要更加注意可见的内容。同时,开发者还应该思考页面缩放级别,例如:当用户放大时,显示更高分辨率的图片是有意义的。

同时,开发者也不能忽视页面性能,较低的图片质量体积也较小,同时也受到文件格式的影响,最终影响页面加载速度和 Web Vitals 核心指标。通过减少加载的图片,则可以减少移动数据的浪费,确保为那些网速慢、设备老旧等的用户创造良好的体验。

因此,一个好的图片方案必须满足以下几个核心要点:

  • 根据视口大小加载不同的图片尺寸,例如:桌面和移动设备
  • 根据视口大小加载不同的图片质量
  • 根据设备像素比 (DPR),缩放级别提供不同质量的图片
  • 提供不同的文件格式,例如:WebP、AVIF 等

2. 一劳永逸的 <picture> 标签

<picture> 元素包含零个或多个 <source> 元素和一个 <img> 元素,用于为不同的显示器、设备场景提供最佳的图像加载方案。

浏览器会思考每个子 <source> 元素,并从中选择最佳匹配。如果没有找到匹配项(或者浏览器不支持 <picture> 元素),则选择 <img> 元素的 src 属性的 URL。最后,所选图像将显示在 <img> 元素占据的空间中。

<picture>
  <source
    // 媒体查询
    media="(-webkit-min-device-pixel-ratio: 1.5)"
    // srcset 指定图片路径和宽度描述符,对应于设备上的原始图像宽度
    // corresponding to the original image width on your device
    srcset="2x-800.jpg 800w, 2x-1200.jpg 1200w, 2x-1598.jpg 1598w"
    // sizes 由媒体查询和插槽宽度组成,也可以使用 vw 表明宽度
    sizes="
      (min-width: 1066px) 743px,
      (min-width: 800px) calc(75vw-57px),
      100vw">
  <img src="1x.jpg" alt="">
   // 用于描述了图片及其渲染的大小
  // 开发者还可以为其添加 alt 、loading="lazy" 等属性
</picture>

在上面的示例代码中,对于旧版本浏览器和低于 1.5x DPR 的屏幕将自动加载 1x.jpg 图片。至于其他屏幕,浏览器会根据视口宽度进行区分,例如:现代手机会加载 2x-800.jpg,台式机则加载 2x-1598.jpg。

为了确定要渲染哪个图片,<picture> 会告知浏览器明确加载开发者希望浏览器加载的内容,即 <source> 定义的内容)。因此,该方案很容易满足如下要求:

  • 根据视口提供不同尺寸的图片
  • 根据视口提供不同质量的图片
  • 根据 DPR / 缩放级别提供不同质量的图片
  • 提供不同的文件格式

从这点来看,开发者最好使用 <picture> 标签来获得响应迅速、高性能、清晰的图片。

不过,切换到 <picture> 最大的缺点就是放弃普通的 <img> 标签,而这意味着需要对现有代码库进行大量的重构。

3. 使用 <img srcset> 替代 <picture>

<picture> 元素的另一种替代方案是使用 <img srcset>。

srcset 属性是一个字符串,用于标识一个或多个图片候选字符串,这些字符串使用逗号 (,) 分隔,每个字符串指定在给定情况下要使用的图片资源。

每个图片候选字符串包含一个图片 URL 和一个可选的宽度 (width) 或像素密度描述符,用于指示应在何种条件下使用该候选图片,而不是使用 src 属性指定的图片。

srcset 以及 sizes 属性是设计响应式网站的关键,两者可以一起使用来制作适合渲染情况的图片的页面。

<img srcset> 和 <picture> 之间的细微差别是:使用 <img> 时,开发者将控制权交给浏览器,由浏览器负责确定最适合加载哪个图片。而由于 <img> 比 <picture> 更常用,因此大多数网站倾向于使用前者来显示响应式图片。

3.1 使用像素密度描述符 (Pixel Density Descriptors) 的方案

像素密度描述符的单位是 “x”,是 dots-per-px 的缩写(dppx) ,反映了设备 DPR。

<img srcset="medium.jpg, large.jpg 2x, ultra.jpg 3x" />
// 或者如下方式 (当浏览器需要 640px、960px 或 1024px 的图片时)
<img srcset="header640.png 640w, header960.png 960w, header1024.png 1024w">

在上面的代码示例中,标准显示器上的浏览器会加载 medium.jpg,而在更高分辨率的显示器上,例如:视网膜显示器、iPhone、现代 Android、4k 显示器等则会加载 large.jpg 或 ultra.jpg。

像素密度描述符 本质是使用一个已经存在了 10+ 年的 trick,即将图片设计为其渲染尺寸 (CSS 尺寸) 的 n 倍大小,例如:图片的固有尺寸为 400×400,而最终渲染尺寸为 200×200。由于浏览器显示时会自动缩小图片,因此看起来很清晰。

如果 “srcset” 中有资源使用了 “w” 描述符,则该 “srcset” 中的所有资源也必须使用 “w” 描述符,此时图片元素的 src 也不被视为候选资源。

<img srcset> 方案有优点也有缺点,例如:

  • ❌ 根据视口提供不同尺寸
  • ❌ 根据视口提供不同质量
  • ☑️️ 根据 DPR / 缩放级别提供不同质量
  • ❌ 可选:提供不同的文件格式

在移动设备上,无差别视口尺寸的图片加载会导致较长下载时间,继而降低网站性能,浪费用户流量,并通过 解码 + 渲染更大的图片 来增加 CPU 负担和增加电池使用量。

注意,开发者还可以使用 CSS 图片集在背景图片中使用密度描述符 image-set,但其也有自己的性能缺陷,因此不提议使用。

background-image: image-set("foo.png" 1x, "foo-2x.png" 2x);

3.2 只使用宽度描述符 width(Width descriptors) 的隐式像素密度方案

width 属性表明当图片被绘制或渲染到任何视觉媒体,例如:屏幕或打印机时,图片以 CSS 像素为单位 的绘制宽度。

  • 如果图片被渲染到视觉媒体,则宽度以 CSS 像素表明
  • 如果图片未渲染到视觉媒体,则其宽度使用图片的自然宽度表明,并根据 naturalWidth 调整视觉媒体显示密度

例如下面的示例,对于宽度不超过 400px 的视口,图片将以 200px 的宽度绘制。否则,将以 400px 的宽度绘制。

<p>Image width: <span class="size">?</span>px (resize to update)</p>
<img
  src="/en-US/docs/Web/HTML/Element/img/clock-demo-200px.png"
  alt="Clock"
  srcset="
    /en-US/docs/Web/HTML/Element/img/clock-demo-200px.png 200w,
    /en-US/docs/Web/HTML/Element/img/clock-demo-400px.png 400w
  "
  // 显式指定 size
  sizes="(max-width: 400px) 200px, 400px" />

接下来就开始介绍目前使用响应式 <img> 最流行的方式:

<img
    src="img.jpg"
    alt=""
	sizes="(max-width: 63.9375em)  100vw,
    	   (min-width:      64em)  750px"
    // 显式指定 size
	srcset="img/content/small.jpg   480w,
			img/content/medium.jpg  750w,
			img/content/large.jpg  1024w"/>

srcset 和 sizes 属性的工作方式与 <source> 标签完全一样,只是没有可用的 media 属性。在上面的代码片段中,为了确定最终的渲染图片,浏览器会使用第一个 size 媒体查询为 true 的结果。假设是 (min-width: 64em) 750px,则相当于告知浏览器渲染最小 750w 的宽度描述符,此时浏览器会加载 medium.jpg,而旧版浏览器会回退到 src。

值得一提的是,宽度描述符也适应了设备的 DPR。接下来一起看看,如果不显式指定 sizes 属性,那么会有什么问题呢?

// 宽度描述符适配 DRP
<img src="small.jpg" srcset="medium.jpg 1000w, large.jpg 2000w" />

根据规范,浏览器会将 宽度描述符除以 <img> 元素的 “源尺寸”,以确定每幅图片的像素密度,然后将其与设备的 DPR(包括缩放级别)进行比较。“源尺寸” 由 sizes 属性定义,如果省略,则默认为 100vw,即当前屏幕宽度。

Firefox 和 Safari 浏览器会选择大于当前 DPR 的密度(如果不可能,则选择最高密度)来确定渲染的图片。Chromium 对 DPR <= 1x 执行一样操作,但对于 DPR> 1x,浏览器会检查值之间的几何平均来确定要加载的图片:

if (next_density < DPR) { continue; /* 跳过下一个 srcset 入口 */ }
if ((DPR <= 1 && DPR> current_density)
  || DPR >= geometric_mean(current_density, next_density))
  return next;
return current;

根据经验法则,对于 DPR > 1x,Chromium 浏览器会选择最接近(srcset 描述的宽度)视口宽度的图片。接下来看一个视口宽度为 360px 的例子,浏览器计算规则如下:

  • 1000w / 360px = 2.8x
  • 2000w / 360px = 5.6x

DPR 为 3x:

  • Firefox 和 Safari 从 slot large.jpg 2000w 中选择密度 5.6x
  • Chrome:
    • 计算几何平均值:Math.sqrt(2.8*5.6) = 4x
    • 3x >= 4x 为 false,因此选择 medium.jpg 1000w

总而言之,由于 宽度描述符隐式代表像素密度描述符,且开发者一般也更加青睐后者。不过,该行为也带来了一些怪异:

  • 第一,由于浏览器的差异且规范将最终图片的决定权留给浏览器。在不同 DPR 、视口宽度、设备等场景下可能加载一样图片,即使在手机上显示得小得多。因此,使用隐式机制会消除显式设置视口大小的能力。
  • 其次,这种方法会导致性能问题,WhatWG 也承认了这一点

33% 的桌面页面会加载超过 83 KB 的多余图片数据,即 srcset 中有一个更好、更小的资源可供使用,但由于 size 属性错误而错过。此外,10% 使用 size 的桌面页面由于 size 属性错误而加载了超过 0.5 MB 的多余图片数据!

总的来说,<img srcset> 方法有以下优势和不足:

  • ☑️️ 根据视口提供不同尺寸
  • ☑️️ 根据视口提供不同质量
  • ☑️️ 根据 DPR / 缩放级别提供不同质量,但是是隐式设置
  • ❌ 可选:提供不同的文件格式

4. 使用布尔值的 <img> 去除隐式像素密度方案

根据前文的学习,开发者可以设置一个 <img> 标签,其结合了 size 和 srcset 的宽度描述符以及隐式密度计算,其行为就像一个 <picture> 元素:

<img
  src="1x.jpg"
  // size 属性
  sizes="
    (max-width: 480px) and (-webkit-device-pixel-ratio: 1)       1px,
    (min-width: 481px) and (-webkit-device-pixel-ratio: 1)       2px,
    (max-width: 480px) and (-webkit-min-device-pixel-ratio: 2)   3px,
    (min-width: 481px) and (-webkit-min-device-pixel-ratio: 2)  16px"
  // srcset 属性
  srcset="
    medium-1x-q75.jpg   1w,
    large-1x-q75.jpg    2w,
    medium-2x-q35.jpg  15w,
    large-2x-q50.jpg   16w"
/>

通过结合 size 和 srcset(包括:宽度描述符),开发者重新获得了对浏览器图片加载的控制权。如前文所述,宽度描述符是隐式工作的,因此还特地引入了 size 属性,该属性针对单个 DPR 以将其明确化。此时 <img> 标签表现与 <picture> 超级类似,从而可以有条件地加载不同尺寸和不同质量的图片。

这种方式可以不改变 DOM 结构,最终显著减少所需的开发时间。当在大型代码库中 “hotfix” 模糊图片时超级重大。

4.1 size 属性

如前文所述,size 属性允许开发者使用 CSS 媒体查询,这意味着其不受最小和最大宽度的限制。此外,Andrea Verlicchi 和 Cloudinary 等也研究了为什么一般不需要区分 2.5 、3 倍甚至更高的分辨率。

对于属性中的条件,一般可以用:

  • 480px 作为断点来区分手机和其他设备类别
  • 使用 -webkit-device-pixel-ratio 和 -webkit-min-device-pixel-ratio 区分 1x DPR 和 >= 2x DPR 屏幕

即使在非 WebKit 浏览器中,-webkit-(min)-device-pixel-ratio 也支持的很好。而如果仅支持 Safari 16+,则可以将前缀替换为 (resolution: 1dppx) 和 (min-resolution: 2dppx)。

实则,开发者只要记住 浏览器只会选择第一个值为 true 的条件,就可以像 if-else 一样编写条件语句:

  • 如果屏幕宽度 <= 480px && DPR == 1:加载插槽 1
  • 如果屏幕宽度 >= 481px && DPR == 1:加载插槽 2
  • 如果屏幕宽度 <= 480px && DPR>= 2:加载插槽 3
  • 如果屏幕宽度 >= 481px && DPR >= 2:加载插槽 16

每个查询的值不再是显示的图片宽度,而是定位的 srcset 插槽。换句话说,删除了 “图片宽度 (image width)” 和 “宽度描述符 (width descriptor)” 之间的近似相关性,而是使用数字来定位 srcset 的特定插槽。

4.2 srcset 属性

如前所述,目前使用来自 sizes 的 px 来设置 “源尺寸”,以便定位 srcset 的特定插槽。结合 “宽度描述符” 内容中提到的隐式密度计算知识,相当于创建了 srcset 映射,诱使浏览器从真实尺寸条件下载图片,即下一个最大的插槽(或最低几何平均值)。

接下来看一个 DPR 为 3x 且屏幕宽度为 360px 的示例:

  • 对于 sizes 属性,(max-width: 480px) 和 (-webkit-min-device-pixel-ratio: 2) 3px 是第一个执行为 true 的媒体查询,此时浏览器将 “源尺寸” 设置为 3px
  • 接下来,浏览器需要定位目标 srcset 插槽
  • 密度:
    • 1w / 3px = 0.33x,2w / 3px = 0.67x
    • 15w / 3px = 5x,16w / 3px = 5.33x
  • 所有浏览器选择的插槽都是 medium-2x-q35.jpg 15w,由于:
    • 下一个最大密度(Firefox、Safari):来自插槽 15w 的 5x
    • Chromium:计算几何平均值:Math.sqrt(5* 5.33) = 5.16x3x >= 5.16x 为假,因此选择插槽 15w

未来可能会有 5x DPR 的设备,因此为了防止图片模糊,需要确保两个 2x DPR 大小值都乘以 5,以便 Safari 和 Firefox 选择正确的 srcset 值(3*5 => 下一个最大值是 15w),而这只会在大于 5x 的屏幕出现之前起作用。

size 和 srcset 中的最后一个插槽基本上是 if-else 链中的 else,这意味着开发者可以简单地 +1 来创建最后一个插槽,供 其他设备 && DPR >= 2 类别使用。

最后,通过使用 src 属性为无法识别任何操作的浏览器设置默认值。如果没有媒体查询匹配,则默认值为 100vw。

5. 关于 “布尔”<img> 的结论

通过使用这种方法,可以安全地判断哪个浏览器或设备渲染了哪个图片。

虽然 <picture> 比这种方法更明确,但使用 “布尔”<img> 标签的工作量比通过引入其他 HTML 标签等方式,例如:更新与 JavaScript 事件和 CSS 相关的依赖项,进行重构要小的多。相比之下,添加或更新 srcset 和 size 这两个属性很容易。

“布尔”<img > 的优点和缺点如下:

  • ☑️️ 根据视口提供不同的尺寸
  • ☑️️ 根据视口提供不同的质量
  • ☑️️ 根据 DPR / 缩放级别提供不同的质量——明确

如果将其自动化,开发者甚至不需要手动制作媒体查询。以下数学公式可以在任何地方实现,其中 N、M 是每个类别的图片宽度,A 是断点号:

<img
  sizes="
    (max-width: Apx) and (resolution: 1dppx) Npx,
    (min-width: (A+1)px) and (resolution: 1dppx) Mpx,
    (max-width: Apx) and (min-resolution: 2dppx) (M+1)px,
    (min-width: (A+1)px) and (min-resolution: 2dppx) (((M+1)*5)+1)px"
  srcset="
    low-dpr-xs.jpg Nw,
    low-dpr-xl.jpg Mw,
    high-dpr-xs.jpg ((M+1)*5)w,
    high-dpr-xl.jpg (((M+1)*5)+1)w"
  src="fallback.jpg"
  alt="don't forget the alt attribute"
/>

参考资料

https://kurtextrem.de/posts/modern-way-of-img#pixel-density-descriptors

https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/srcset

https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/width

https://css-tricks.com/responsive-images-youre-just-changing-resolutions-use-srcset/

https://html.spec.whatwg.org/multipage/images.html#update-the-source-set

https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/sizes

https://developer.mozilla.org/en-US/docs/Web/HTML/Element/picture

© 版权声明

相关文章

1 条评论

您必须登录才能参与评论!
立即登录
  • 头像
    三意康养 读者

    ie6时期 img 可以挂病毒

    无记录