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

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

Vladimir Levin
Vladimir Levin

发布时间:2020 年 8 月 5 日

借助 content-visibility 属性,用户代理可以跳过元素的渲染工作(包括布局和绘制),直到需要时再进行渲染。由于系统会跳过渲染,因此如果您的大部分内容不在屏幕上,使用 content-visibility 属性可让初始用户加载速度加快很多。还可以更快地与屏幕上的内容互动。很不错。

浏览器支持

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

来源

演示:图表显示网络结果
在我们的文章演示中,将 content-visibility: auto 应用于分块内容区域可在初始加载时将渲染性能提升 7 倍。请继续阅读,了解详情。

CSS Containment

CSS 容器的主要目标是通过可预测地将 DOM 子树与网页的其余部分隔离,从而提升网页内容的渲染性能。

基本上,开发者可以告知浏览器页面的哪些部分封装为一组内容,让浏览器无需考虑子树之外的状态即可推断内容。知道哪些内容(子树)包含隔离的内容意味着浏览器可以针对网页呈现做出优化决策。

CSS 容器有四种类型,每种类型都是 contain CSS 属性的可能值,可以组合为以空格分隔的值列表:

  • size:元素的尺寸包含能够确保无需检查其后代即可布局元素的框。这意味着,如果我们只需要元素的尺寸,则可以跳过子元素的布局。
  • layout:布局控制意味着子项不会影响页面上其他框的外部布局。这样一来,如果我们只想排列其他框,则可以跳过子孙布局。
  • style:样式封装可确保影响的不仅仅是其后代,而且不会逃出元素(例如计数器)的属性。这样一来,如果我们只需要计算其他元素的样式,就可以跳过针对后代的样式计算。
  • paint: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 步中,浏览器会处理所有内容,查找可能已发生更改的内容。它会更新所有新元素的样式和布局,以及可能因新更新而发生偏移的元素。这是渲染工作。这需要一些时间。

旅行博客的屏幕截图。
一个旅游博客示例。请参阅 Codepen 上的演示

现在考虑一下,如果对博客中的每个故事都加上 content-visibility: auto 会发生什么。一般循环相同:浏览器下载并渲染网页的部分内容。不过,区别在于它在第 2 步中执行的工作量。

借助 content-visibility,它将设置当前对用户可见的所有内容(它们位于屏幕上)的样式和布局。不过,在处理完全超出屏幕范围的短片故事时,浏览器会跳过渲染工作,而只会设置元素框本身的样式和布局。

加载此页面的效果就像其中包含完整的屏幕内故事,以及每个屏幕外故事对应的空白框。这样做性能会好很多,预计可将加载的渲染开销降低 50% 或更多。在我们的示例中,渲染时间从 232 毫秒缩短到了 30 毫秒。性能提升了 7 倍

您需要做些什么工作才能获得这些好处?首先,我们将内容分块:

一张带注释的屏幕截图,显示了如何使用 CSS 类将内容分块为多个部分。
通过应用 story 类将内容分成若干部分的示例,以接收 content-visibility: auto。查看 Codepen 上的演示

然后,我们将以下样式规则应用于这些部分:

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

使用 contain-intrinsic-size 指定元素的自然尺寸

为了实现 content-visibility 的潜在优势,浏览器需要应用尺寸限制,以确保内容的呈现结果不会以任何方式影响元素的大小。也就是说,该元素的布局方式与空元素一样。如果元素在常规的块布局中未指定高度,则其高度为 0。

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

幸运的是,CSS 提供了另一个属性 contain-intrinsic-size如果元素受尺寸限制影响,该属性可以有效指定元素的自然尺寸。在我们的示例中,我们将其设置为 1000px,作为版块的高度和宽度估算值。

这意味着,它将像只有一个尺寸为“intrinsic-size”的子元素一样进行布局,从而确保未设尺寸的 div 仍会占用空间。contain-intrinsic-size 充当占位符大小,取代渲染内容。

contain-intrinsic-sizeauto 关键字会导致浏览器记住上次渲染的尺寸(如果有),并使用该尺寸,而不是开发者提供的占位符尺寸。例如,如果您指定了 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 规范意味着您的 CSS 文件将带来一些激动人心的性能提升。如需详细了解这些属性,请参阅: