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 子树与页面的其余部分隔离,从而提高 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 等样式特征的 landmark 元素在屏幕外时也会出现在无障碍树中,因为浏览器只有在进入视口后才会呈现这些样式。为防止这些元素显示在无障碍功能树中(可能会导致杂乱),请务必添加 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 以防止其显示,但保持其缓存状态。这样一来,当视图再次变为活跃状态时,便可快速呈现。

对下一次绘制的交互的影响 (INP)

INP 是一个指标,用于评估网页可靠响应用户输入的能力。主线程上发生的任何过多工作(包括渲染工作)都可能会影响响应速度。

只要能减少任意给定页面上的渲染工作,主线程便有机会更快地响应用户输入。这包括渲染工作,在适当情况下使用 content-visiblity CSS 属性可以减少渲染工作,尤其是在启动期间(此时完成了大部分渲染和布局工作)。

减少渲染工作会对 INP 产生直接影响。当用户尝试与正确使用 content-visibility 属性来推迟布局和渲染屏幕外元素的网页互动时,您将有机会让主线程响应用户可见的关键工作。在某些情况下,这可以提高网页的 INP。

总结

content-visibility 和 CSS Containment 规范意味着,您的 CSS 文件将获得一些令人振奋的性能提升。如需详细了解这些属性,请参阅: