跳转到内容

字体与图片优化:font-display、picture 与 AVIF/WebP

发布于 更新于

一句话: font-display CSS 描述符控制在网络字体加载期间文本是保持不可见还是以 回退字体渲染,而 <img> 元素的 loading 属性控制何时获取图片、fetchpriority 提示其相对优先级,AVIF、WebP 等格式则改变这次获取需要传输多少数据。

用 font-display 控制字体加载期间的可见性

Section titled “用 font-display 控制字体加载期间的可见性”

根据 MDN,font-display 为 @font-face 规则定义了一个阻塞期(浏览器为等待字体而 隐藏文本的时长)和一个交换期(字体到达后仍会被换入的时长)。swap 值给出极短的 阻塞期,因此文本几乎立即以回退字体渲染,网络字体到达后再换入:

@font-face {
font-family: "Brand Sans";
src: url("/fonts/brand-sans.woff2") format("woff2");
font-weight: 400;
font-display: swap;
}

根据 MDN,font-display 属于 Baseline 广泛可用,自 2020 年 1 月起获得支持。

根据 MDN,<img> 元素的 loading 属性中,lazy 会将加载推迟到图片被计算为接近 视口时,而默认值 eager 会立即加载;这种延迟仅在启用 JavaScript 时才生效——当脚本 被禁用时,loading="lazy" 不会产生任何效果。fetchpriority 属性则单独提示该次获取的 相对优先级:

<img
src="/images/hero.avif"
width="1200"
height="630"
loading="eager"
fetchpriority="high"
alt="Product hero shot"
/>
<img
src="/images/gallery-3.avif"
width="400"
height="300"
loading="lazy"
alt="Gallery photo 3"
/>

根据 MDN,每张图片都建议显式设置 width 与 height,对 loading="lazy" 的图片尤其 如此:没有这两个属性的图片在加载完成前布局尺寸为 0×0,而根据 MDN,一张浏览器认为 没有尺寸的未加载图片,如果它从未与某个可见元素的区域相交,就可能永远不会加载。

根据 MDN,font-display 属于 Baseline 广泛可用。对于图片,<img> 的 loading 与 fetchpriority 在当前浏览器中已被广泛支持。AVIF 在 Chrome、Edge 与 Opera 中受支持, Firefox 从 Firefox 93 起、Safari 从 Safari 16.1 起为其添加了支持——支持历史比 WebP 更短; 根据 MDN,WebP 在 Chrome、Edge、Firefox、Opera 与 Safari 的当前版本中均受支持,因此格式 回退链对 AVIF 而言仍然重要,尤其是面向使用较旧浏览器版本的用户时。

用 AVIF 和 WebP 搭配 JPEG 回退提供图片

Section titled “用 AVIF 和 WebP 搭配 JPEG 回退提供图片”

根据 MDN,AVIF 在质量相近的情况下通常比 JPEG 小约 50%,WebP 的有损模式通常比 JPEG 小 25%-35%。<picture> 包裹一个或多个 <source> 元素,并搭配一个回退用的 <img>:

<picture>
<source srcset="/images/hero.avif" type="image/avif" />
<source srcset="/images/hero.webp" type="image/webp" />
<img src="/images/hero.jpg" width="1200" height="630" alt="Product hero shot" />
</picture>

根据 MDN,HTMLImageElement.decode() 返回一个 Promise,图片准备好可用时会 resolve, 解码失败时则会以 EncodingError reject。出现这种情况可能是因为图片请求失败、调用 decode() 后请求发生变化,或图片数据已损坏。代码可以在这次尝试被 reject 时选择回退, 但仅凭这次 reject 不能确定浏览器缺少 AVIF 解码支持:

let avifSupportPromise;
function canDecodeAvifProbe() {
if (avifSupportPromise) return avifSupportPromise;
if (!('Image' in window)) {
avifSupportPromise = Promise.resolve(false); // 没有 Image 构造函数:跳过探测。
return avifSupportPromise;
}
const probe = new Image();
probe.src =
'data:image/avif;base64,AAAAIGZ0eXBhdmlmAAAAAGF2aWZtaWYxbWlhZk1BMUIAAADybWV0YQAAAAAAAAAoaGRscgAAAAAAAAAAcGljdAAAAAAAAAAAAAAAAGxpYmF2aWYAAAAADnBpdG0AAAAAAAEAAAAeaWxvYwAAAABEAAABAAEAAAABAAABGgAAAB0AAAAoaWluZgAAAAAAAQAAABppbmZlAgAAAAABAABhdjAxQ29sb3IAAAAAamlwcnAAAABLaXBjbwAAABRpc3BlAAAAAAAAAAEAAAABAAAAEHBpeGkAAAAAAwgICAAAAAxhdjFDgQAMAAAAABNjb2xybmNseAACAAIABoAAAAAXaXBtYQAAAAAAAAABAAEEAQKDBAAAAB9tZGF0EgAKCBgABogQEDQgMgkxIABJSElBQkJB';
avifSupportPromise = probe
.decode()
.then(() => true)
.catch(() => false); // 本次尝试失败:选择回退图片。
return avifSupportPromise;
}
  • 根据 MDN,通过 <picture> 或其他方式为 AVIF 提供 WebP、JPEG 或 PNG 回退—— AVIF 需要完整下载后才能渲染任何内容,且它在当前浏览器中的支持历史比 WebP 更短。
  • 为每张图片设置 width 与 height,尤其是 loading="lazy" 的图片,这样浏览器 才有已知的布局尺寸,图片才能被找到并加载。
  • 根据 MDN,fetchpriority 只是向浏览器提示相对优先级,而非保证——因此把 high 留给最重要的那些图片。
  • 根据 MDN,font-display 的默认值(auto)由浏览器自行决定阻塞行为;如果想要 swap 或 optional 所定义的那种极短阻塞期,需要显式设置。
  • 参见资源提示了解 rel="preload"。