本文介绍CSS的新属性 content-visibility
,这个属性可以显著提升渲染性能。
Chromium 85
开始有了 [content-visibility](<https://drafts.csswg.org/css-contain/#propdef-content-visibility>)
属性,这可能是对于提升页面加载性能提升最有效的CSS属性,content-visibility
让用户代理正常情况下跳过元素渲染工作(包括 layout
和 painting
),除非需要的时候进行渲染工作。如果页面有大量离屏(off-screen
)的内容,借助 content-visibility
属性可以跳过离屏内容的渲染,加快用户首屏渲染时间,可以做到减少的页面可交互的等待时间
<center>上图中使用了 content-visibility: auto
属性的内容部分,其首次加载渲染(Rendering
)提升了近7倍</center>
content-visibility
属性依赖于 the CSS Containment Spec , Containment 浏览器兼容性见 most modern browsers.
下面先介绍一下该属性的在浏览器中的兼容性
CSS Containment首先,提升页面渲染性能最关键是预测页面独立 DOM
节点树。
开发者需要告诉浏览器哪些内容是封装到一个容器的,允许浏览器对内容进行推断,而不需要考虑该容器子树之外的状态。从而知道哪些内容占位符是独立的容器,意味着浏览器可以对它进行优化。
这儿有四种类型的 CSS containment
size
- 元素上的尺寸属性值确保可以在不检查其后代的情况下展开元素的框。意味着我们如果只需要元素容器的尺寸就可以跳过子树的布局(
layout
)
- 元素上的尺寸属性值确保可以在不检查其后代的情况下展开元素的框。意味着我们如果只需要元素容器的尺寸就可以跳过子树的布局(
layout
layout
布局是指后代不会影响页面上其他框的外部布局。如果我们想要做的是布局其他容器,那么这允许我们潜在地跳过子树的布局。
style
- 确保容器对后代以外的其他属性产生影响的属性不会转义元素(例如计数器)。如果我们想计算其他元素上的样式,这允许我们潜在地跳过后代元素的样式计算。
paint
- 确保容器的后代不会显示在其边界之外,没有任何东西可以明显地溢出元素,如果一个元素不在屏幕上或不可见,它的后代也将不可见。如果元素在屏幕外,这允许我们跳过绘制后代元素。
content-visibility
跳过渲染可能很难确定 containment
包含哪些值,因为浏览器优化可能只有在指定了适当的集合时才会启动。你可以看看哪些值搭配 what works best 性能最优,或者使用 content-visibility
属性,它将自动进行优化。作为开发者使用 content-visibility
可以达到事半功倍的效果。
content-visibility
属性有多个值,auto
可以立即改善性能,一个元素是 style
配置为:
content-visibility: auto
与 contain
配置为 layout
、style
、paint
等属性效果一样,如果一个元素离屏(与用户无关的元素——指选中或者已经上焦点),它获取 size
属性值(它将阻止了 painting 和 hit-testing )。
以上表述是什么意思呢?简短来说是:如果一个元素离屏,它的后代不会被渲染,浏览器计算元素大小而无须考虑它的内容,大部分的渲染是无须考虑后代的 style
和 layout
。
当元素接近视口时,浏览器不再给容器添加 size
,并开始绘制和点击测试元素的内容。渲染可以即时完成,让用户马上看到视图。
https://player.bilibili.com/player.html?aid=586109787
content-visibility
这个例子中,我们可以看到未使用 content-visibility:auto
属性的页面渲染时间是232ms,使用了content-visibility:auto
属性的渲染时间是30ms
这个旅行博客比较典型的包含了一系列的故事和图片,以及对故事的描述,下面是当导航到这个博客的时候发生的事情:
- 页面从网络上下载需要的资源
- 根据用户的可见性,计算
style
和 布局页面的内容 - 浏览器继续执行第一步直到页面所有资源都下载完毕
在第二步中,浏览器处理所有可能发生变化的内容,它更新任何新元素的样式和布局,以及可能由于新更新而发生变化的元素。渲染工作需要时间。
现在想想如果给元素的 style
添加属性发生了什么,
content-visibility: auto
浏览器下载和渲染每一小块,他们之间的不同之处是在上面的第2步的执行。
使用 content-visibility
,浏览器将计算显示在屏幕上的 style
和 layout
,如果故事全屏了,浏览器将跳过渲染工作仅仅计算容器的 style
和 layout
。
页面加载性能包括所有的视图内故事和离屏的空容器,渲染性能将提升50%以上,在我们的例子中渲染时间从232ms减小到30ms,提升了7倍左右。
为了获得这些好处,你需要做哪些工作?
首先,我们将内容分成几个部分:
示例将每个部分添加 story
class,应用 content-visibility:auto
,示例在线地址
我们应用的 style
如下:
.story { content-visibility: auto; contain-intrinsic-size: 1000px; /* Explained in the next section. */ }
指定元素的大小注意,随着内容进入和离开可见性,它将根据需要开始和停止渲染。然而,这并不意味着浏览器将不得不反复渲染和重新渲染相同的内容,因为渲染工作将尽可能的情况下保存。
contain-intrinsic-size
为了更好的从 content-visibility
收益,浏览器应用 size
来确保渲染的内容尺寸不受其他影响,意味着元素将 进行 layout
计算即使元素为空,如果元素没有高度,那么默认高度为 0 px。
这可能并不理想,因为滚动条的大小会发生变化,这取决于每个故事的高度不为零。
此时, CSS提供了另外一个属性 contain-intrinsic-size
,它可以配置元素的占位大小,在我们例子中将它设为了 1000px
作为分块的高度和宽度。
这意味着它将像拥有“内在大小”维度的一个子元素一样进行布局,以确保未设置大小的 div
仍然占据空间。content -intrinsic-size
充当占位符大小,代替呈现的内容。
content-visibility: hidden
如果您想保持内容未呈现(不管它是否在屏幕上),同时利用缓存呈现状态的好处,那么使用
content-visibility: hidden;
content-visibility: hidden
属性对 带来的好处跟 content-visibilit: auto
对于离屏元素带来的好处是一样的,不同点是,它不会像 auto
一样自动的渲染到屏幕上。
这样可以给你更好的实现以下场景:
控制元素的隐藏,稍后又显示
与其他隐藏元素的方法进行比较:
display:none
- 隐藏元素并且销毁渲染状态,意味着从隐藏到显示的成本与渲染一个新元素是一样的
visibility: hidden
- 隐藏元素并且保存渲染状态,它并不是真的从文档中移除元素,因为它(和它的子树)仍然占据页面上的几何空间,仍然可以单击。它还会在任何需要的时候更新呈现状态,即使是隐藏的时候。
content-visibility: auto
- 隐藏元素并且保存渲染状态,如果有变化发送,让元素显示出来的方法是移除 property
content-visibility: atuo
- 隐藏元素并且保存渲染状态,如果有变化发送,让元素显示出来的方法是移除 property
内容可见性的一些很好的用例:
content-visibility: auto
可用于实现高级虚拟卷轴和测量布局。
CSS 中 content-visibility
和 CSS Containment 规格对于提升性能有显著作用,更多的文章请查看: