文章目录
- 一、前言
- 二、浏览器支持程度
- 三、CSS Containment
- 四、跳过具有 content-visibility 的渲染工作
- 五、关于accessibility的说明
- 六、使用contains-intrinsic-size指定元素的自然大小
- 七、通过content-visibility: hidden隐藏内容
- 八、对下一幅画的交互影响(INP)
一、前言
content-visibility:新的CSS属性,可以提高渲染性能。
通过跳过屏幕外内容的呈现来改善初始加载时间。
在Chromium 85中推出的content-visibility属性可能是对提高页面加载性能最有影响的新CSS属性之一。内容可见性允许用户代理跳过元素的呈现工作,包括布局和绘制,直到需要它为止。因为会跳过呈现,如果大部分内容不在屏幕上,那么利用content-visibility属性可以使初始用户加载速度更快。它还允许与屏幕上的内容进行更快的交互。很整洁。
在我们的文章演示中,将content-visibility: auto应用于分块内容区域,可以在初始加载时将呈现性能提高7倍。
二、浏览器支持程度
content-visibility 依赖于CSS Containment Spec中的原语。虽然 content-visibility 目前仅在Chromium 85中得到支持(并且被认为是Firefox的“值得原型化”),但大多数现代浏览器都支持Containment Spec。
三、CSS Containment
CSS Containment 的关键和首要目标是通过提供可预测的DOM子树与页面其余部分的隔离来提高web内容的呈现性能。
基本上,开发人员可以告诉浏览器页面的哪些部分被封装为一组内容,从而允许浏览器推断内容,而无需考虑子树之外的状态。知道哪些内容(子树)包含孤立的内容意味着浏览器可以为页面呈现做出优化决策。
CSS包含有四种类型,每种类型都是包含CSS属性的潜在值,它们可以组合在一个空格分隔的值列表中:
size: 元素的size限制确保元素的方框可以在不检查其后代的情况下进行布局。这意味着如果我们只需要元素的大小,我们可以跳过后代元素的布局。
layout: 布局遏制意味着后代不会影响页面上其他框的外部布局。如果我们想要做的只是布局其他的盒子,这就允许我们跳过后代的布局。
style: 样式包含确保那些可以对多个子类产生影响的属性不会逃逸元素(例如计数器)。如果我们想要的只是计算其他元素的样式,这就允许我们跳过后代元素的样式计算。
paint: paint containment确保包含框的后代不会显示在其边界之外。没有任何东西可以明显地溢出元素,如果一个元素不在屏幕上或以其他方式不可见,它的后代也将不可见。如果元素在屏幕外,这允许我们跳过绘制后代。
四、跳过具有 content-visibility 的渲染工作
可能很难确定要使用哪些包含值,因为浏览器优化可能只有在指定了适当的集合后才会启动。您可以随意使用这些值,看看哪个效果最好,或者您可以使用另一个称为content-visibility的CSS属性来自动应用所需的包含。内容可见性确保您以开发人员的最小努力获得浏览器所能提供的最大性能收益。
content-visibility属性接受多个值,但auto是能够立即提供性能改进的值。具有内容可见性:auto的元素将获得布局、样式和油漆包含。如果元素不在屏幕上(与用户相关的元素不相关的是那些在子树中有焦点或选择的元素),它也会获得大小限制(并且停止绘制和命中测试其内容)。
这是什么意思?简而言之,如果元素不在屏幕上,则不会呈现其后代。浏览器在不考虑元素内容的情况下决定元素的大小,并在此停止。大多数呈现,比如元素子树的样式和布局都会被跳过。
当元素接近视口时,浏览器不再添加大小限制,而是开始绘制和测试元素的内容。这使得渲染工作能够及时完成,使用户能够看到。
PS:
如果您还注意不调用任何DOM API来强制在被跳过的子树上进行某些呈现,则浏览器只能跳过呈现工作。如果您正在使用内容可见性来提高性能,请审计代码以确保这些api没有被调用。为了帮助找到它们,如果您为具有内容可见性:hidden的元素的子树调用这些api之一,Chromium将打印控制台消息。要查看消息,请打开详细日志记录。
五、关于accessibility的说明
content-visibility: auto的特性之一是,屏幕外的内容在文档对象模型中仍然可用,因此在可访问性树中仍然可用(与visibility: hidden不同)。这意味着,可以在页面上搜索和导航内容,而无需等待加载或牺牲呈现性能。
然而,另一方面,具有诸如display: none或visibility: hidden等样式特性的地标性元素在离开屏幕时也会出现在可访问性树中,因为浏览器在进入视口之前不会呈现这些样式。为了防止这些在可访问性树中可见,可能导致混乱,一定要添加aria-hidden=“true”。
PS:
在Chromium 85-89中,content-visibility: auto内的非屏幕子元素被标记为不可见。特别是,headings 和 landmark roles 没有暴露在可访问性工具中。在Chromium 90中,更新了这一点,以便暴露它们。
六、使用contains-intrinsic-size指定元素的自然大小
为了实现content-visibility的潜在好处,浏览器需要应用大小控制,以确保内容的呈现结果不会以任何方式影响元素的大小。这意味着元素将被布置为空。如果元素没有在常规块布局中指定高度,那么它的高度将为0。
这可能不太理想,因为滚动条的大小会发生变化,这取决于每个故事的高度非零。
值得庆幸的是,CSS提供了另一个属性,container-intrinsic-size,如果元素受到大小包含的影响,它可以有效地指定元素的自然大小。在我们的例子中,我们将其设置为1000px作为部分的高度和宽度的估计值。
这意味着它的布局就好像它有一个“固有大小”维度的子元素,确保未调整大小的div仍然占用空间。contains -intrinsic-size作为占位符大小代替呈现的内容。
在Chromium 98及以后的版本中,有一个新的auto关键字用于contains-intrinsic-size。指定后,浏览器将记住最后呈现的大小(如果有的话),并使用该大小而不是开发人员提供的占位符大小。例如,如果你指定了include-intrinsic-size: auto 300px,元素将在每个维度上以300px的固有大小开始,但是一旦元素的内容被渲染,它将保持渲染的固有大小。任何后续的渲染大小更改也将被记住。在实践中,这意味着如果您滚动一个应用了content-visibility: auto的元素,然后将其滚动回屏幕外,它将自动保持其理想的宽度和高度,而不会恢复到占位符大小。这个特性对于无限滚动条特别有用,现在可以随着用户浏览页面的时间自动改进大小估计。
我们可以使用IntersectionObserver和MutationObserver为每个元素设置正确的内联大小。Alex Russell解释了在没有滚动条的情况下内容可见性是如何工作的,以及调整大小弹性的内容可见性修复。
七、通过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来防止它们被显示,但保持它们的缓存状态。这使得视图在再次激活时能够快速渲染。
在一项实验中,Facebook工程师观察到,当返回到先前缓存的视图时,导航时间提高了250毫秒。
八、对下一幅画的交互影响(INP)
INP是评估页面可靠地响应用户输入的能力的指标。主线程上发生的任何过多的工作(包括呈现工作)都可能影响响应性。
只要可以减少任何给定页面上的呈现工作,主线程就有机会更快地响应用户输入。这包括呈现工作,在适当的地方使用content- visibility CSS属性可以减少呈现工作,尤其是在启动期间,此时大部分呈现和布局工作已经完成。
减少渲染工作对INP有直接影响。当用户试图与正确使用content-visibility属性来延迟屏幕外元素的布局和呈现的页面进行交互时,就给了主线程一个响应关键的用户可见工作的机会。在某些情况下,这可以提高页面的INP。