创建有效的图像组件

图像组件封装了最佳实践性能,并提供了一种开箱即用的解决方案来优化图像。
图像是 Web 应用程序性能遇到瓶颈的主要原因,也是需要优化的重点领域。未优化的图像会导致页面膨胀,目前在第 90个百分位占总页面权重的 70% 以上(以字节为单位)。多种优化图像的方法需要一个智能的“图像组件”,其中包含默认的性能解决方案。

Aurora团队用Next.js建立一个这样的部件。目标是创建一个优化的图像模板,Web 开发人员可以进一步自定义。该组件是一个很好的模型,并为在其他框架、内容管理系统 (CMS) 和技术堆栈中创建图像组件设定了标准。我们已经为 Nuxt.js开发了一个类似的组件,并且我们正在与Angular合作以在未来版本中进行图像优化。这篇文章讨论了我们如何设计 Next.js Image 组件以及我们在此过程中所总结的经验教训。

图像优化问题和机会

图像不仅会影响性能,还会影响业务。页面上的图像数量是用户访问网站的转化率的第二大预测因素。用户转换的会话比未转换的会话少 38% 的图像。作为其最佳实践审核的一部分,Lighthouse 列出了多种优化图像和改进网络重要信息的机会。图像会影响到核心 Web 生命特征和用户体验的一些常见领域具体如下:

未调整大小的图像损坏 CLS

未指定大小的图像会导致布局不稳定并导致高累积布局偏移 ( CLS )。在img元素上设置widthheight 属性可以防止布局偏移。例如:

<img src="flower.jpg" width="360" height="240">

宽度和高度应设置为使渲染图像的纵横比接近其自然纵横比。纵横比的显着差异可能会导致图像失真。允许在 CSS 中指定纵横比的一个相对较新的属性可以相应调整图像大小,同时防止 CLS。

大图像可能会损害 LCP

图像的文件大小越大,下载所需的时间就越长。大图像可以是页面的“英雄”图像,也可以是视口中负责触发最大内容绘制 ( LCP )的最重要元素。作为关键部分并且需要很长时间下载的图像会延迟 LCP。

在许多情况下,开发人员可以通过更好的压缩和使用响应式图像来减小图像大小。元素<img>srcsetsizes 属性有助于提供不同大小的图像文件。然后浏览器可以根据屏幕大小和分辨率选择合适的图像文件。

不合格的图像压缩会损害 LCP

AVIFWebP等现代图像格式可以提供比常用 JPEG 和 PNG 格式更好的压缩。在某些情况下,对于相同质量的图像,更好的压缩可以将文件大小减少 25% 到 50%。这种减少导致下载速度更快,数据消耗更少。该应用程序应支持这些格式的浏览器提供现代图像格式。

加载不必要的图像会损害 LCP

加载页面时,不会向用户显示折叠下方或不在视口中的图像。可以推迟它们,这样它们就不会对 LCP 产生反应并延迟LCP。延迟加载可用于加载用户滚动页面时的图像。

优化挑战

由于前面列出的问题,团队可以评估性能成本,并实施最佳实践解决方案来克服它们。然而,这在实践中通常不会发生,低效的图像继续拖慢网络速度。可能的原因包括:

  • 优先事项:Web 开发人员通常倾向于关注代码、JavaScript 和数据优化。因此,他们可能不知道图像的问题或如何优化它们。由设计师创建或用户上传的图像在优先级列表中可能不高。
  • 开箱即用的解决方案:即使开发人员意识到图像优化的细微差别,但缺乏适用于其框架或技术堆栈的多合一开箱即用解决方案也可能是一种阻碍。
  • 动态图像:除了作为应用程序一部分的静态图像之外,动态图像还由用户上传或来自外部数据库或 CMS。在图像源是动态的情况下,定义此类图像的大小可能具有挑战性。
  • 标记过载:包含图像大小或srcset 不同大小的解决方案需要为每个图像添加额外的标记,这可能很乏味。该srcset 属性于 2014 年推出,但如今仅被 26.5%的网站使用。使用srcset 时,开发人员必须创建各种尺寸的图像。诸如just-gimme-an-img 之类的工具可以提供帮助,但必须手动使用每个图像。
  • 浏览器支持:现代图像格式如 AVIF 和 WebP 创建较小的图像文件,但需要在不支持它们的浏览器上进行特殊处理。开发人员必须使用诸如内容协商或元素,以便为所有浏览器提供图像。
  • 延迟加载的复杂性:有多种技术和库可用于为非折叠图像实现延迟加载。选择最好的可能是一个挑战。开发人员也可能不知道从“折叠”加载延迟图像的最佳距离。设备上不同的视口尺寸会使这进一步复杂化。
  • 不断变化的格局:随着浏览器开始支持新的 HTML 或 CSS 功能以提高性能,开发人员可能难以评估它们中的每一个。例如,Chrome 正在引入Priority Hints功能作为Origin Trial。它可用于提高页面上特定图像的优先级。总体而言,如果在组件级别评估和实施此类增强功能,开发人员会发现这样操作更容易。

图像组件作为解决方案

可用于优化图像的机会以及为每个应用程序单独实施它们的挑战使我们产生了图像组件的想法。图像组件可以封装和实施最佳实践。通过用图像组件替换<img> 元素,开发人员可以更好地解决他们的图像性能问题。

在过去的一年里,我们一直在使用Next.js框架来设计和实现他们的Image 组件。它可以用作<img> Next.js 应用程序中现有元素的替代品,如下所示。

// Before with <img> element:
function Logo() {
  return <img src="/logo.jpg" alt="logo" height="200" width="100" />
}

// After with image component:
import Image from 'next/image'

function Logo() {
  return <Image src="/logo.jpg" alt="logo" height="200" width="100" />
}

该组件试图通过一组丰富的功能和原则来解决与图像相关的问题。它还包括允许开发人员根据各种图像要求对其进行自定义的选项。

防止布局变化

如前所述,未调整大小的图像会导致布局偏移并导致 CLS。使用 Next.js Image 组件时,开发人员必须 使用widthheight 属性提供图像大小,以防止任何布局偏移。如果大小未知,开发人员必须指定layout=fill 提供一个位于特定大小容器内的非固定形状的图像。或者,你可以使用静态图像导入在创建时检索硬盘驱动器上实际图像的大小并将其包含在图像中。

// Image component with width and height specified
<Image src="/logo.jpg" alt="logo" height="200" width="100" />

// Image component with layout specified
<Image src="/hero.jpg" layout="fill" objectFit="cover" alt="hero" />

// Image component with image import
import Image from 'next/image'
import logo from './logo.png'

function Logo() {
  return <Image src={logo} alt="logo" />
}

由于开发人员不能使用未调整大小的图像组件,因此设计无疑会让他们花时间考虑图像大小并防止布局偏移。

促进响应

要使图像跨设备响应,开发人员必须在<img> 元素中设置srcsetsizes 属性。我们希望通过 Image 组件减少这项工作。我们设计了 Next.js Image 组件保证每个应用程序只需设置一次属性值。我们根据布局模式将它们应用于 Image 组件的所有实例并提出了一个由三部分组成的解决方案:

  1. deviceSizes 属性:此属性可用于根据应用程序用户群通用的设备一次性配置断点。断点的默认值包含在配置文件中。
  2. imageSizes property:这也是一个可配置的属性,用于获取与设备大小断点对应的图像大小。
  3. 每个图像中的layout 属性:用于指示如何使用每个图像的deviceSizesimageSizes 属性。布局模式支持的值是fixedfillintrinsicresponsive

当使用响应填充 布局模式请求图像时,Next.js 根据请求页面的设备的大小识别要提供的图像,并适当地设置图像中srcsetsizes

下面的比较展示了如何使用布局模式来控制不同屏幕上的图像大小。我们使用了Next.js 文档中共享的演示图像,在手机和标准笔记本电脑上查看。

笔记本电脑屏幕 手机屏幕

Layout = Intrinsic:缩小以适应容器在较小视口上的宽度。在较大的视口上放大时不会超过图像的固有尺寸。容器宽度为 100%

布局 = 固定:图像没有响应。宽度和高度是固定的,类似于 元素,不管它被呈现在什么设备上。

Layout = Responsive:根据容器在不同视口上的宽度缩小或放大,保持纵横比。

布局 = 填充:宽度和高度被拉伸以填充父容器。(父`
本例中宽度设置为300*500)

为不同布局渲染的图像

提供内置延迟加载

Image 组件默认提供了一个内置的、高性能的延迟加载解决方案。使用<img> 元素时,有一些本地选项可用于延迟加载,但它们都有缺点,使用起来很棘手。开发人员可能会采用以下延迟加载方法之一:

  • 指定loading 属性:这很容易实现,但目前在某些浏览器上不支持
  • 使用Intersection Observer API:创建自定义延迟加载解决方案需要付出努力以及深思熟虑的设计和操作,开发人员不可能总是有充裕的时间。
  • 导入第三方库以延迟加载图像:可能需要额外的操作来评估和集成合适的第三方库以进行延迟加载。

在 Next.js Image 组件中,加载"lazy" 默认设置为。延迟加载是使用 Intersection Observer 实现的,它可用于大多数现代浏览器。开发人员不需要做额外的事来启用它,但他们可以在需要时禁用它。

预加载重要图片

很多时候,LCP 元素是图像,大图像会延迟 LCP。预加载关键图像是个好主意,这样浏览器就可以更快地发现该图像。使用<img> 元素时,预加载提示可能包含在 HTML 标题中,如下所示。

<link rel="preload" as="image" href="important.png">

一个设计良好的图像组件应该提供一种方法来调整图像的加载顺序,而不管使用的框架如何。在 Next.js 图像组件的情况下,开发人员可以使用图像组件的priority 属性指示适合预加载的图像。

<Image src="/hero.jpg" alt="hero" height="400" width="200" priority />

添加priority 属性简化标记,使用起来更方便。图像组件开发人员还可以探索应用启发式方法来自动预加载页面上满足特定标准的首屏图像的选项。

鼓励高性能图像托管

图像 CDN被推荐用于自动图像优化,它们还支持现代图像格式,如 WebP 和 AVIF。Next.js Image 组件默认使用加载器架构的图像 CDN 。以下示例显示加载器允许在 Next.js 配置文件中配置 CDN。

module.exports = {
  images: {
    loader: 'imgix',
    path: 'https://ImgApp/imgix.net',
  },
}

通过这种配置,开发者可以在图片源中使用相对URL,框架会将相对URL与CDN路径连接起来生成绝对URL。支持流行的图像 CDN,如ImgixCloudinaryAkamai。该架构通过为应用程序实现自定义loader 功能来支持使用自定义云提供商。

支持自托管图片

可能存在网站无法使用图像 CDN 的情况。在这种情况下,图像组件必须支持自托管图像。Next.js 图像组件使用图像优化器作为内置图像服务器,提供类似 CDN 的 API。如果安装在服务器上,优化器会使用Sharp进行生产图像转换。对于希望创建自己的图像优化管道人来说,这个库是一个不错的选择。

支持渐进式加载

渐进式加载是过去一种通过在实际图像加载时显示质量明显较低的占位符图像来了解用户兴趣的技术。它提高了感知性能并增强了用户体验。它可以与延迟加载结合使用,用于折叠下方的图像或折叠上方的图像。

Next.js Image 组件支持通过placeholder属性对图像进行渐进式加载。这可以用作LQIP(低质量图像占位符),也可用在加载实际图像时显示低质量或模糊的图像。

影响

结合上述所有优化,我们已经看到 Next.js Image 组件在生产中取得成功,并且还在类似的图像组件上与其他技术堆栈合作。

Leboncoin 将他们的旧 JavaScript 前端迁移到 Next.js 时,他们还升级了他们的图像管道以使用 Next.js Image 组件。在从<img> 迁移到 next/image的页面上,LCP 从 2.4s 下降到 1.7s。为页面下载的总图像字节从 663kB 增加到 326kB(延迟加载的图像字节约为 100kB)。

经验教训

任何创建 Next.js 应用程序的人都可以从使用 Next.js Image 组件进行优化中受益。但是,如果你想为另一个框架或 CMS 创建类似的抽象性能,以下是我们在此过程中学到的一些可能会对你有所帮助的经验教训。

安全阀弊大于利

在 Next.js Image 组件的早期版本中,我们提供了一个unsized 属性,允许开发人员略过尺寸要求,并使用未指定尺寸的图像。我们认为在无法提前知道图像的高度或宽度的情况下,这是必要的。但是,我们注意到用户在 GitHub 问题中推荐unsized 属 性作为解决大小要求问题的全方位解决方案,即使他们认为这是不会使 CLS 恶化的解决方式。我们随后弃用并删除了该unsized 属性。

摆脱有关有用摩擦的无意义顾虑

调整图像大小的要求是“有用摩擦”的一个例子。它限制了组件的使用,但它提供了巨大的性能优势作为交换。如果用户清楚地了解潜在的性能优势,他们将很容易接受这种约束。因此,这值得在有关该组件的文档和其他已发布材料中解释这种权衡。

但是,你可以在不牺牲性能的情况下找到解决此类摩擦的方法。例如,在 Next.js Image 组件的开发过程中,我们收到投诉说查找本地存储图像的大小让人恼火。我们添加了静态图像导入,通过使用 Babel 插件在创建时自动检索本地图像的尺寸来简化此过程。

在便利功能和性能优化之间取得平衡

如果你的图像组件除了对用户施加“有用的摩擦”之外什么都不做,开发人员往往不想使用它。我们发现,尽管图像大小和自动生成srcset 值等性能特征是最重要的,面向开发人员的便利功能,如自动延迟加载和内置模糊占位符,同样会引起人们对 Next.js Image 组件的兴趣。

为功能制定路线图以推动采用

创建一个适用于所有情况的解决方案非常困难。很容易设计出适合 75% 的人的东西,然后告诉其他 25% 的人“在这些情况下,这个组件不适合你”。

在实践中,这种策略与作为组件设计师的你目标不一致。你希望的是开发人员采用你的组件以便从其性能优势中受益。如果有一群用户无法迁移并感到被排除在对话之外,则很难做到这一点。他们可能会表达失望,从而导致消极观点影响采纳。

建议为你的组件制定一个路线图,涵盖长期内所有合理的用例。它还有助于在文档中明确说明不支持的内容以及原因,以便对组件打算解决的问题设置期望。

结论

图像使用和优化很复杂。开发人员必须权衡性能和图像质量之间的关系,同时确保出色的用户体验。这使得图像优化成为一项高成本、高影响力的工作。

我们没有让每个应用程序每次都重新发明轮子,而是想出了一个最佳实践模板,开发人员、框架和其他技术堆栈可以将其用作他们自己实现的参考。当我们支持其他框架的图像组件时,这种经验确实很有价值。

Next.js Image 组件成功地提高了 Next.js 应用程序的性能结果,从而增强了用户体验。我们相信这是一个很好的模型,可以在更广泛的生态系统中良好运行,欢迎提出更好的意见。

原文作者 Leena Sohoni Kara Erickson Alex Castle
原文链接 https://web.dev/image-component/

推荐阅读
作者信息
AgoraTechnicalTeam
TA 暂未填写个人简介
文章
175
相关专栏
本专栏仅用于分享音视频相关的技术文章,与其他开发者和 Agora 研发团队交流、分享行业前沿技术、资讯。发帖前,请参考「社区发帖指南」,方便您更好的展示所发表的文章和内容。