2022 年 5 月,Aurora 和 Angular 团队宣布,他们将共同为 Angular 开发图片指令。该指令最近作为 Angular v14.2 的一部分发布了开发者预览版。本文介绍了新的图片指令 NgOptimizedImage
如何在 Angular 中支持图片优化。
背景
图片是 Web 用户体验的常见且关键组成部分,99.9% 的网页都会生成一个或多个图片的请求。图像也是导致网页重量过大的主要原因,平均每个网页的图像大小为 982 千字节。
由于图像数量和大小不断增加,因此可能会影响网页的性能并影响 Core Web Vitals 指标。在 2021 年,79.4% 的桌面版网页的 Largest Contentful Paint (LCP) 元素是图片。因此,追求优化后的图片已成为许多人的不懈追求。
Aurora 团队相信,可以利用框架的强大功能,为开发者常见的挑战提供内置解决方案。他们首次涉足图片优化领域的是 Next.js 图片组件。他们将此组件视为测试平台,以便确定改进图片优化的开发者体验 (DX) 能否让更多使用框架的应用获得更出色的性能。
Next.js 用户 Leboncoin 的第一组结果令人鼓舞。在开始使用 next/image
后,Leboncoin 的 LCP 显著改善(从 2.4 秒缩短到 1.7 秒)。社区随后采用 next/image
有助于提高符合 LCP 阈值的 Next.js 源的数量。很快,其他框架中就出现了请求提供类似功能,其中之一就是 Angular。
因此,Aurora 咨询了 Angular 和 Nuxt,为这些框架设计了图片组件原型。Nuxt 图片组件于去年发布。现在,Angular 图片指令 (NgOptimizedImage
) 已发布,可将图片优化默认设置为 Angular。
机会
Angular 是当今开发者使用的领先 JavaScript 框架之一。HTTPArchive 在移动设备上抓取的超过 5 万个源都使用了该库,并且 NPM 上的每周下载量高达近 300 万次。
从 Core Web Vitals 得分来看,符合“良好”LCP 阈值的 Angular 来源所占的百分比仍有待提高。2022 年 6 月,只有 18.74% 的 Angular 网站在移动设备上的 LCP 得分较高。由于超过 70% 的移动版和桌面版网页的 LCP 元素是图片,因此未优化的 LCP 图片可能是导致 Angular 网站 LCP 较差的主要原因之一。
Angular 图片指令旨在帮助提高这些指标。
NgOptimizedImage 指令的 MVP
Angular 图片指令的 MVP 基于 Aurora 迄今为止构建的图片组件中总结出的经验,同时将设计调整为适应 Angular 的客户端渲染体验。许多标准的图片优化问题已通过以下任一方式得到解决:
- 提供强大的默认值。
- 抛出错误或警告,以确保符合最佳实践。
设计亮点如下:
智能延迟加载
在网页加载时对用户不可见的图片(例如,折叠下方的图片或隐藏的轮播界面图片)最好采用延迟加载方式。延迟加载可释放浏览器资源,以加载其他关键文本、媒体或脚本。大多数图片都不是关键图片,应采用延迟加载方式,但在 2021 年,只有 7.8% 的网页使用了原生延迟加载。
Angular 图片指令默认延迟加载非关键图片,并且仅提前加载特别标记为
priority
的图片。这样可以确保大多数图片都能以最佳方式加载。确定关键图片的优先级
添加资源提示(例如
preload
或preconnect
)来优先加载关键图片,这是推荐的最佳实践。不过,大多数应用都没有使用它们。根据《2021 年网络年鉴》,只有 12.7% 的移动网页使用预连接提示,只有 22.1% 的移动网页使用预加载提示。将图片标记为优先级图片后,图片指令会在两个方面发挥作用。
- 它会将图片的 fetchpriority 设置为
"high"
,以便浏览器知道应以高优先级下载图片。 - 在开发模式下,运行时检查会确认是否已包含与图片来源对应的
preconnect
资源提示。
在开发模式下,该指令还会使用 PerformanceObserver API 来验证 LCP 图片是否已按预期标记为
priority
。如果未标记为priority
,系统会抛出错误,指示开发者向 LCP 图片添加priority
属性。最终,这种自动化与合规性的结合可确保 LCP 图片具有
preconnect
提示、fetchpriority
属性值为high
,并且不会延迟加载。- 它会将图片的 fetchpriority 设置为
针对常用图片工具进行了优化的配置
建议 Angular 应用使用图片 CDN,它们通常默认提供优化服务。
该指令通过提供在应用中配置图片 CDN 的特别吸引人的开发者体验 (DX),鼓励使用图片 CDN。它支持加载器 API,可让您在配置中定义 CDN 提供程序和基本网址。配置完成后,您只需在标记中定义资源名称即可。例如,
// in module providers: provideImgixLoader('https://mysite.net/assets/') // in markup <img ngSrc="image.png" > <img ngSrc="image2.png" >
这相当于添加以下图片标记,并减少了开发者必须为每张图片添加的标记。
<img src="https://mysite.net/assets/image.png"> <img src="https://mysite.net/assets/image2.png">
image 指令为最常用的图片 CDN 提供了内置加载器的最佳配置。这些加载器会自动设置图片网址的格式,以确保为每个 CDN 使用推荐的图片格式和压缩设置。
内置错误和警告
除了上述内置优化之外,该指令还具有内置检查,以确保开发者在图片标记中遵循了推荐的最佳实践。image 指令会执行以下检查。
未设尺寸的图片:如果图片标记未明确定义宽度和高度,图片指令会抛出错误。未调整大小的图片可能会导致布局偏移,进而影响网页的累积布局偏移 (CLS) 指标。为防止出现这种情况,建议的最佳实践是,图片应指定
width
和height
属性。宽高比:如果 HTML 中定义的
width
:height
的宽高比与渲染的图片的实际宽高比不接近,图片指令会抛出错误,以告知开发者。这可能会导致图片在屏幕上看起来失真。以下情况可能会导致这种情况:- 您不小心定义了错误的尺寸(宽度或高度),或者
- 如果您在 CSS 中按百分比定义了尺寸,但未定义另一个尺寸(例如,
width: 100%
需要height: auto
才能确保图片在两个尺寸上都放大)。
超大图片:如果图片未定义
srcset
,并且内在图片明显大于渲染的图片,该指令将显示一条警告,建议使用srcset
和sizes
属性。图片密度:如果您尝试在
srcset
中添加像素密度超过3x
的图片,该指令会抛出错误。通常不建议使用高于2x
的描述符,因为这会导致高分辨率移动设备下载巨大的图片,从而产生意外后果。此外,人眼实际上无法区分 2 倍以上的差异。
挑战
在设计 NgOptimizedImage
时,将图像优化策略调整为在客户端框架中运行是一项主要挑战。Next.js 上的默认呈现体验是服务器端呈现 (SSR) 或静态网站生成 (SSG),而 Angular 上的默认呈现体验是客户端呈现 (CSR)。尽管 Angular 支持 SSR 库 angular/universal,但大多数 Angular 应用(约 60%)都使用 CSR。
image 指令完全是为 CSR 而构建的,以符合 Angular 应用中的典型用例。这增加了额外的约束条件,因此该团队不得不重新考虑如何为 CSR 应用构建特定优化。
遇到的一些挑战如下:
支持资源提示
预加载关键素材资源有助于浏览器更早发现这些资源。不过,在 Angular 应用中添加资源提示很复杂,因为:
手动添加:开发者很难手动添加
preload
资源提示。Angular 会为整个项目或网站中的所有路由使用一个共享的 index.html 文件。因此,每个路线的文档<head>
都是相同的(至少在投放时是相同的)。向<head>
添加任何preload
提示意味着系统会为所有路由预加载资源,即使不需要该资源也是如此。因此,不建议手动添加preload
提示。渲染期间自动添加:在 CSR 应用中,使用该框架在渲染期间向文档的标头添加预加载提示没有帮助。由于呈现是在下载和执行 JavaScript 后进行的,因此
<head>
的呈现时间过晚,没有任何价值。对于该指令的第一个版本,
preconnect
和fetchpriority
提示的组合可用于优先显示图片,而非preload
。不过,Aurora 目前正在与 Angular CLI 团队合作,以便在构建时自动注入资源提示。敬请期待!优化服务器上的图片大小和格式
由于 Angular 应用通常在客户端呈现,因此文件系统中的图片无法在请求时压缩,而是按原样提供。因此,建议使用图片 CDN 来压缩图片,并根据需要将其转换为 WebP 等新型格式或 AVIF。
虽然该指令不会强制使用图片 CDN,但我们强烈建议您将其与 CDN 搭配使用,并确保使用该指令的内置加载器来使用正确的配置选项。
影响
以下演示展示了 Angular 图片指令对图片性能的影响。它会比较以下两个网站:
网站一:使用通过 Imgix CDN 提供的图片的原生 <img>
元素(使用默认配置选项)。
网站二:对所有图片使用 image 指令。其中还包括指令抛出的警告或错误直接建议的优化。
该团队与合作伙伴合作,验证了图片指令对真实企业 Angular 应用的性能影响。
其中一个合作伙伴就是 Land's End。我们希望他们的网站能成为一个很好的测试用例,以便了解真实应用可能会出现的结果。
在使用图片指令之前和之后,他们对质量检查环境进行了 Lighthouse 实验室测试。在桌面设备上,其 LCP 中位数从 12.0 秒缩短到了 3.0 秒,LCP 提升了 75%。在移动设备上,LCP 中位数从 20.2 秒缩短至 12.0 秒(改善幅度为 40.6%)。
未来路线图
这只是 Angular 图片指令设计的第一部分。我们计划在未来版本中提供许多其他功能,包括:
更好地支持自适应图片:
NgOptimizedImage
目前支持使用srcset
,但必须为每张图片手动提供srcset
和sizes
属性。未来,该指令可以自动生成srcset
和sizes
属性。自动注入资源提示
您或许可以与 Angular CLI 集成,为重要的 LCP 图片生成预连接和预加载代码。
支持 Angular SSR
MVP 版本的设计充分考虑了 Angular CSR 约束条件,但探索适用于 Angular SSR (angular/universal) 的图片优化解决方案也很重要。
开发者体验改进
NgOptimizedImage
要求为每张图片指定width
和height
属性。不过,对于某些开发者来说,为每张图片指定这些信息可能会很麻烦。在下一次迭代中,我们有望改进此处的开发者体验,具体如下:- 支持一种不需要明确定义宽度/高度的额外模式(类似于 Next.js 中的“
fill
”图片布局选项)。 - 使用 CLI 集成通过确定图片的实际尺寸,自动为本地图片设置宽度和高度。
- 支持一种不需要明确定义宽度/高度的额外模式(类似于 Next.js 中的“
总结
Angular 图片指令将分阶段面向开发者提供,从 v14.2.0 中的开发者预览版开始。欢迎试用 NgOptimizedImage
并留下反馈!
特别感谢 Katie Hempenius 和 Alex Castle 的贡献。