通过跳过屏幕外内容的呈现,缩短初始加载时间。
发布时间:2020 年 8 月 5 日
通过 content-visibility
属性,用户代理可以跳过元素的渲染工作(包括布局和绘制),直到需要这些信息为止。由于系统会跳过渲染,因此如果您的大部分内容不在屏幕上,使用 content-visibility
属性可让初始用户加载速度加快很多。还可以更快地与屏幕上的内容互动。很不错。
CSS Containment
CSS 包含性的关键以及总体目标是通过以可预测的方式将 DOM 子树与页面的其余部分隔离,从而提高 Web 内容的渲染性能。
从本质上讲,开发者可以告知浏览器网页的哪些部分封装为一组内容,以便浏览器推理内容,而无需考虑子树之外的状态。知道哪些内容(子树)包含隔离的内容意味着浏览器可以针对网页渲染做出优化决策。
CSS 容器有四种类型,每种类型都是 contain
CSS 属性的可能值,可以组合为以空格分隔的值列表:
size
:元素的尺寸包含能够确保无需检查其后代即可布局元素的框。这意味着,如果我们只需要元素的尺寸,则可以跳过子元素的布局。layout
:布局包含表示子级不会影响页面上其他框的外部布局。这样一来,如果我们只想布置其他框,就可以跳过后代的布局。style
:样式包含可确保可能会对不仅仅对其后代产生影响的属性(例如计数器)不会对该元素进行转义。这样一来,如果我们只想计算其他元素的样式,则可以跳过对后代元素的样式计算。paint
:绘制容器可确保包含盒子的后代不会显示在其边界之外。任何内容都不能明显溢出元素,如果某个元素位于屏幕之外或以其他方式不可见,其子元素也将不可见。这样一来,如果元素不在屏幕上,我们就可以跳过绘制其子元素。
使用 content-visibility
跳过渲染工作
可能很难确定要使用哪些包含值,因为只有在指定了适当的值集时,浏览器优化才会启动。您可以尝试使用这些值,看看哪种值最有效,也可以使用 content-visibility
自动应用所需的容器。content-visibility
可确保您作为开发者,只需付出极少的努力,即可获得浏览器可提供的最大性能提升。
content-visibility 属性接受多个值,但 auto
可立即提升性能。具有 content-visibility: auto
的元素会获得 layout
、style
和 paint
容器。如果元素位于屏幕之外(并且与用户没有其他关联,相关元素是指其子树中具有焦点或处于选中状态的元素),则它也会获得 size
容器(并且会停止绘制和对其内容进行点击测试)。
这是什么意思?简而言之,如果元素不在屏幕上,其子元素将不会呈现。浏览器会在不考虑其任何内容的情况下确定元素的大小,然后停止计算。系统会跳过大多数渲染,例如元素子树的样式和布局。
当元素接近视口时,浏览器不再添加 size
容器,并开始绘制和点击测试元素的内容。这样,系统就可以在用户看到内容时及时完成渲染工作。
无障碍功能注意事项
content-visibility: auto
的一项功能是,屏幕外内容仍可在文档对象模型中使用,因此可在无障碍功能树中使用(与 visibility: hidden
不同)。这意味着,您可以在页面上搜索内容并导航到该内容,而无需等待其加载或牺牲渲染性能。
不过,具有 display: none
或 visibility: hidden
等样式特征的 landmark 元素在屏幕外时也会出现在无障碍树中,因为浏览器只有在进入视口后才会呈现这些样式。为防止这些元素显示在无障碍功能树中(可能会导致杂乱),请务必添加 aria-hidden="true"
。
示例:旅游博客
旅游博客通常包含一系列故事和几张图片,以及一些描述性文字。下面是在典型浏览器中导航到旅游博客时会发生的情况:
- 系统会从网络下载网页的一部分以及所有所需资源。
- 浏览器会设置网页的所有内容的样式并进行布局,而不会考虑这些内容是否对用户可见。
- 浏览器会返回第 1 步,直到下载完所有网页和资源。
在第 2 步中,浏览器会处理所有内容,查找可能已发生更改的内容。它会更新所有新元素的样式和布局,以及可能因新更新而发生变化的元素。这是渲染工作。这需要时间。
现在,考虑一下如果您在博客中的每个单独故事中都添加 content-visibility: auto
,会发生什么情况。一般循环相同:浏览器下载并渲染网页的部分内容。不过,区别在于它在第 2 步中执行的工作量。
借助 content-visibility,它将设置当前对用户可见的所有内容(它们位于屏幕上)的样式和布局。不过,在处理完全位于屏幕之外的故事时,浏览器会跳过渲染工作,仅设置元素框本身的样式和布局。
加载此页面的效果就像其中包含完整的屏幕内故事,以及每个屏幕外故事对应的空白框。这样做性能会好很多,预计可将加载的渲染开销降低 50% 或更多。在我们的示例中,渲染时间从 232 毫秒缩短到了 30 毫秒。性能提升了 7 倍。
您需要做些什么工作才能获得这些好处?首先,我们将内容分块:
然后,我们将以下样式规则应用于这些部分:
.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-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
以防止其显示,但保持其缓存状态。这样一来,当视图再次变为活跃状态时,便可快速呈现。
对下一次绘制的交互的影响 (INP)
INP 是一个指标,用于评估网页可靠响应用户输入的能力。主线程上发生的任何过多工作(包括渲染工作)都可能会影响响应速度。
只要能减少任意给定页面上的渲染工作,主线程便有机会更快地响应用户输入。这包括渲染工作,在适当情况下使用 content-visiblity
CSS 属性可以减少渲染工作,尤其是在启动期间(此时完成了大部分渲染和布局工作)。
减少渲染工作会对 INP 产生直接影响。当用户尝试与正确使用 content-visibility
属性来推迟布局和渲染屏幕外元素的网页互动时,您将有机会让主线程响应用户可见的关键工作。在某些情况下,这可以提高网页的 INP。
总结
content-visibility
和 CSS Containment 规范意味着,您的 CSS 文件将获得一些令人振奋的性能提升。如需详细了解这些属性,请参阅: