content-visibility:提升渲染性能的新 CSS 属性

通过跳过屏幕外内容的渲染来缩短初始加载时间。

发布时间:2020 年 8 月 5 日

content-visibility 属性允许用户代理跳过元素的渲染工作,包括布局和绘制,直到需要时才进行。由于跳过了渲染,如果您的内容很大一部分在屏幕外,则使用 content-visibility 属性可以使初始用户加载速度更快。它还可以更快地与屏幕上的内容进行交互。非常棒。

浏览器支持

  • Chrome: 85.
  • Edge: 85.
  • Firefox: 125.
  • Safari: 18.

来源

demo with figures representing network results
在我们的文章演示中,将 content-visibility: auto 应用于分块内容区域,可在初始加载时提供 7 倍 的渲染性能提升。请继续阅读以了解更多信息。

CSS Containment(CSS 包含)

CSS 包含的关键和总体目标是通过提供 DOM 子树与页面其余部分的预测性隔离 来提高 Web 内容的渲染性能。

基本上,开发人员可以告诉浏览器页面的哪些部分被封装为一组内容,从而允许浏览器在无需考虑子树外部状态的情况下推理内容。了解哪些内容位(子树)包含隔离的内容意味着浏览器可以为页面渲染做出优化决策。

有四种类型的 CSS 包含,每种都是 contain CSS 属性的潜在值,可以以空格分隔的值列表组合在一起

  • size:元素上的尺寸包含确保元素的框可以在无需检查其后代的情况下进行布局。这意味着如果我们只需要元素的大小,我们就可以潜在地跳过后代的布局。
  • layout:布局包含意味着后代不会影响页面上其他框的外部布局。这允许我们潜在地跳过后代的布局,如果我们只想布局其他框。
  • style:样式包含确保可能对其后代产生影响的属性不会逸出元素(例如,计数器)。这允许我们潜在地跳过后代的样式计算,如果我们只想计算其他元素上的样式。
  • paint:绘制包含确保包含框的后代不会在其边界之外显示。没有任何内容可以明显地溢出元素,并且如果元素在屏幕外或以其他方式不可见,则其后代也将不可见。这允许我们潜在地跳过绘制后代,如果元素在屏幕外。

使用 content-visibility 跳过渲染工作

可能很难弄清楚要使用哪些包含值,因为浏览器优化可能仅在指定适当的集合时才会启动。您可以尝试这些值以查看 哪些效果最佳,或者您可以使用 content-visibility 来自动应用所需的包含。content-visibility 确保您可以获得浏览器可以提供的最大性能提升,而您作为开发人员只需付出最小的努力。

content-visibility 属性接受多个值,但 auto 是提供即时性能改进的值。具有 content-visibility: auto 的元素获得 layoutstylepaint 包含。如果元素在屏幕外(并且与用户无关——相关元素将是在其子树中具有焦点或选择的元素),它还会获得 size 包含(并且它停止 绘制命中测试 其内容)。

这意味着什么?简而言之,如果元素在屏幕外,则不会渲染其后代。浏览器确定元素的大小而不考虑其任何内容,并且就此停止。大多数渲染,例如元素子树的样式设置和布局都被跳过。

当元素接近视口时,浏览器不再添加 size 包含,并开始绘制和命中测试元素的内容。这使得渲染工作能够及时完成,以便用户看到。

关于无障碍功能的说明

content-visibility: auto 的一项功能是屏幕外内容仍然可以在文档对象模型中使用,因此,无障碍功能树(与 visibility: hidden 不同)。这意味着,可以在页面上搜索内容并导航到内容,而无需等待其加载或牺牲渲染性能。

但是,另一方面,具有样式功能(例如 display: nonevisibility: hidden)的 地标 元素在屏幕外时也会出现在无障碍功能树中,因为浏览器直到它们进入视口才会渲染这些样式。为了防止这些元素在无障碍功能树中可见,可能导致混乱,请务必添加 aria-hidden="true"

示例:旅行博客

在此示例中,我们以右侧的旅行博客为基准,并在左侧的分块区域应用 content-visibility: auto。结果显示,初始页面加载时的渲染时间从 232 毫秒 降至 30 毫秒

旅行博客通常包含一组故事,其中包含一些图片和一些描述性文字。以下是典型浏览器导航到旅行博客时发生的情况

  1. 页面的部分内容与任何需要的资源一起从网络下载。
  2. 浏览器对页面的所有内容进行样式设置和布局,而不考虑内容是否对用户可见。
  3. 浏览器返回步骤 1,直到下载页面的所有内容和资源。

在步骤 2 中,浏览器处理所有内容,查找可能已更改的内容。它更新任何新元素的样式和布局,以及可能因新更新而移动的元素。这是渲染工作。这需要时间。

A screenshot of a travel blog.
旅行博客示例。请参阅 Codepen 上的演示

现在考虑一下,如果您在博客中的每个单独的故事上放置 content-visibility: auto 会发生什么。一般循环是相同的:浏览器下载并渲染页面的块。但是,不同之处在于步骤 2 中完成的工作量。

使用 content-visibility,它将对当前对用户可见的所有内容(它们在屏幕上)进行样式设置和布局。但是,在处理完全在屏幕外的故事时,浏览器将跳过渲染工作,而仅对元素框本身进行样式设置和布局。

加载此页面的性能将如同它包含屏幕上的完整故事和每个屏幕外故事的空框一样。这表现得更好,与加载的渲染成本相比,预计减少 50% 或更多。在我们的示例中,我们看到渲染时间从 232 毫秒 提升到 30 毫秒。这是一个 7 倍 的性能提升。

为了获得这些好处,您需要做哪些工作?首先,我们将内容分块为 sections

An annotated screenshot of chunking content into sections with a CSS class.
将内容分块为 sections 的示例,应用了 story 类,以接收 content-visibility: auto。请参阅 Codepen 上的演示

然后,我们将以下样式规则应用于 sections

.story {
  content-visibility: auto;
  contain-intrinsic-size: 1000px; /* Explained in the next section. */
}

使用 contain-intrinsic-size 指定元素的自然大小

为了实现 content-visibility 的潜在好处,浏览器需要应用尺寸包含,以确保内容的渲染结果不会以任何方式影响元素的大小。这意味着元素将像它是空的一样布局。如果元素在常规块布局中未指定高度,则其高度将为 0。

这可能不是理想的,因为滚动条的大小会发生变化,这取决于每个故事是否具有非零高度。

值得庆幸的是,CSS 提供了另一个属性 contain-intrinsic-size,它可以有效地指定元素的自然大小,如果元素受到尺寸包含的影响。在我们的示例中,我们将其设置为 1000px,作为 sections 的高度和宽度的估计值。

这意味着它的布局将如同它有一个“intrinsic-size”尺寸的单个子元素一样,确保您未设置尺寸的 div 仍然占用空间。contain-intrinsic-size 充当替代渲染内容的占位符大小。

auto 关键字用于 contain-intrinsic-size,使浏览器记住上次渲染的大小(如果有),并使用该大小而不是开发人员提供的占位符大小。例如,如果您指定 contain-intrinsic-size: auto 300px,则元素将以每个维度 300px 的固有尺寸开始,但是一旦元素的内容被渲染,它将保留渲染的固有尺寸。随后的任何渲染尺寸更改也将被记住。在实践中,这意味着如果您滚动应用了 content-visibility: auto 的元素,然后将其滚动回屏幕外,它将自动保留其理想的宽度和高度,而不会恢复为占位符尺寸。此功能对于无限滚动器特别有用,无限滚动器现在可以随着用户浏览页面而自动改进尺寸估计。

使用 content-visibility: hidden 隐藏内容

如果您希望保持内容未渲染,无论它是否在屏幕上,同时利用缓存渲染状态的好处,该怎么办?输入:content-visibility: hidden

content-visibility: hidden 属性为您提供与屏幕外 content-visibility: auto 相同的未渲染内容和缓存渲染状态的所有好处。但是,与 auto 不同,它不会自动开始在屏幕上渲染。

这为您提供了更多控制权,使您可以隐藏元素的内容,并在以后快速取消隐藏它们。

将其与其他隐藏元素内容的常用方法进行比较

  • display: none:隐藏元素并销毁其渲染状态。这意味着取消隐藏元素与渲染具有相同内容的新元素一样昂贵。
  • visibility: hidden:隐藏元素并保留其渲染状态。这并不能真正从文档中删除元素,因为它(及其子树)仍然占用页面上的几何空间,并且仍然可以单击它。它还在需要时更新渲染状态,即使在隐藏时也是如此。

content-visibility: hidden,另一方面,隐藏元素,同时保留其渲染状态,因此,如果需要发生任何更改,它们仅在元素再次显示时发生(即,content-visibility: hidden 属性被删除)。

content-visibility: hidden 的一些出色用例是在实现高级虚拟滚动器和测量布局时。它们也非常适合单页应用程序 (SPA)。不活动的应用程序视图可以保留在 DOM 中,并应用 content-visibility: hidden 以防止其显示但保持其缓存状态。这使得视图在再次变为活动状态时可以快速渲染。

对 Interaction to Next Paint (INP) 的影响

INP 是一项指标,用于评估页面可靠响应用户输入的能力。响应能力可能会受到主线程上发生的任何过度工作量的影响,包括渲染工作。

每当您可以减少任何给定页面上的渲染工作时,您都在为主线程提供更快响应用户输入的机会。这包括渲染工作,并且在适当的情况下使用 content-visiblity CSS 属性可以减少渲染工作,尤其是在启动期间,此时会完成大多数渲染和布局工作。

减少渲染工作对 INP 有直接影响。当用户尝试与页面交互时,该页面正确使用了 content-visibility 属性来延迟屏幕外元素的布局和渲染,您为主线程提供了响应关键的用户可见工作的机会。在某些情况下,这可以改善页面的 INP。

结论

content-visibility 和 CSS Containment Spec 意味着一些令人兴奋的性能提升即将到来,直接进入您的 CSS 文件。有关这些属性的更多信息,请查看