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

通过跳过屏幕外内容的呈现,缩短了初始加载时间。

Vladimir Levin
Vladimir Levin

在 Chromium 85 中推出后,content-visibility 属性可能是对提升网页加载性能影响最大的新 CSS 属性之一。content-visibility 使用户代理能够跳过某个元素的渲染工作(包括布局和绘制),直到需要用到它为止。由于会跳过渲染,因此如果内容的大部分内容在屏幕之外,利用 content-visibility 属性可显著加快初始用户加载速度。它还有助于更快地与屏幕上的内容互动。非常简洁。

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

浏览器支持

浏览器支持

  • 85
  • 85
  • 124

来源

content-visibility 依赖于 CSS Containment 规范中的基元。虽然 content-visibility 目前仅在 Chromium 85 中受支持(对于 Firefox,也被认为是“值得进行原型设计”),大多数现代浏览器都支持 Containment 规范。

CSS 包含

CSS 包含的主要目标是,通过以可预测的方式将 DOM 子树与网页的其余部分隔离开,从而提高 Web 内容的渲染性能。

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

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

  • size:元素的大小包含可确保该元素的框可以布局,而无需检查其后代。这意味着,如果只需要元素的大小,我们有可能跳过后代的布局。
  • layout:布局包含表示子项不会影响页面上其他框的外部布局。这样一来,如果我们只想布置其他框,我们就可以跳过后代的布局。
  • style:样式包含可确保不只影响其后代的属性(例如计数器)不会转义相应元素。这样,如果我们只想计算其他元素的样式,就可以跳过后代的样式计算。
  • paint:绘制包含可确保包含框的后代不会在其边界之外显示。任何内容都不能明显溢出该元素,如果某个元素在屏幕之外或因其他原因不可见,其后代也将不可见。这样一来,我们可以在元素位于屏幕外时跳过绘制后代的步骤。

使用 content-visibility 跳过渲染工作

可能很难分辨要使用哪些包含值,因为浏览器优化可能只有在指定了适当的集合值时才会启动。您可以调整这些值以查看哪个效果最佳,也可以使用另一个名为 content-visibility 的 CSS 属性自动应用所需的包含项。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 步中的工作量。

借助内容可见性,它将为用户当前可见的所有内容(用户在屏幕上)设置样式和布局。不过,在处理完全脱离屏幕的故事时,浏览器会跳过渲染工作,只设置元素框本身的样式和布局。

加载此页面时,加载时好像包含完整的屏幕故事,以及每个屏幕外故事的空白框。这样做的效果要好得多,加载的渲染费用预计会降低 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,作为这些区段的高度和宽度的估算值。

这意味着,其布局就像具有“固有尺寸”维度的单个子级一样,从而确保未调整大小的 div 仍然占用空间。 contain-intrinsic-size 充当占位符尺寸,以代替呈现的内容。

在 Chromium 98 及更高版本中,contain-intrinsic-size 有一个新的 auto 关键字。指定后,浏览器将记住上次渲染的尺寸(如果有),并使用该尺寸而不是开发者提供的占位符尺寸。例如,如果您指定了 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 文件即将实现一些令人兴奋的性能提升。如需详细了解这些属性,请查看: