移除css属性-可用于性能优化的 CSS 属性:content-visibility

最近内容可见性已经实际运用在业务中,优化一些渲染性能。

这是一个比较新的属性,功能也很强大。 本文将带领您深入了解它。

什么是内容可见性?

内容可见性:该属性控制元素是否呈现其内容,这允许用户代理(浏览器)在需要时可能省略大量布局和呈现工作。

MDN 原文:Content-Visibility CSS 属性控制元素是否渲染其内容,同时强制执行一组强大的包含,允许用户代理停止潜在地省略大量布局和渲染工作,直到需要为止。基本上可以让用户代理跳过元素的渲染工作(包括布局和绘制),直到需要为止 - 这使得初始页面加载快得多。

它有几个共同的价值观。

/* Keyword values */
content-visibilityvisible;
content-visibilityhidden;
content-visibilityauto;

我来分别解释一下:

包含内在尺寸

其实除了content-visibility之外,还有一个匹配的属性——contain-intrinsic-size。

contains-intrinsic-size:控制内容可见性指定的元素的自然大小。

前两个属性仅查看它们的定义和介绍就有点令人困惑。

我们先来看看content-visibility具体是如何使用的。

内容可见性:可见是默认值。 添加后没有任何效果,我们直接跳过。

通过内容可见性优化显示切换性能:隐藏

首先,我们来看看 content-visibility:hidden。 一般用来和display:none进行比较,虽然它们之间还是有很大的区别。

首先,假设我们有两个 DIV 包装盒:

<div class="g-wrap">
    <div>1111</div>
    <div class="hidden">2222</div>
</div>

将两个 div 设置为 200x200 的红色块:

.g-wrap > div {
    width200px;
    height200px;
    background#000;
}

疗效如下:

好的,没问题。 接下来,我们将 content-visibility:hidden 设置为 .hidden 并看看会发生什么:

.hidden {
    content-visibility: hidden;
}

疗效如下:

注意,仔细看效果,这里添加了 content-visibility:hidden 后,只有添加该元素的 div 子元素消失了,而父元素本身及其样式仍然存在于页面中。

如果我们使用 content-visibility:hidden 设置删除元素本身的宽度、高度、padding、margin 和其他属性,则该元素将在页面上消失,就像设置了 display:none 一样。

那么,content-visibility:hidden 的作用是什么?

对于设置了 content-visibility:hidden 的元素,其子元素将被隐藏; 但是,其渲染状态将被缓存。 因此,当删除 content-visibility:hidden 时,用户代理不需要从头开始渲染它及其子元素。

为此,如果我们将该属性应用到一些一开始需要隐藏,但随后需要在页面某个时刻渲染的元素移除css属性,或者一些需要频繁切换显示和隐藏状态的元素,渲染效率会有特别大的提升。

使用 content-visibility:auto 实现延迟加载或虚拟列表

好的,下面是内容可见性的核心用法,借助 auto 属性值。

content-visibility:auto 的作用是,如果元素不在屏幕上且与用户不相关,则不会渲染其后代元素。 是不是和LazyLoad很相似?

我们来看一个这样的DEMO来了解它的功能:

假设,我们有这样一个HTML结构,里面富含很多文本内容:

<div class="g-wrap">
    <div class="paragraph">...</div>
    // ... 包含了 N 个 paragraph
    <div class="paragraph">...</div>
</div>

各.paragraph的内容如下:

为此,整个页面如下所示:

因为我们还没有对页面的内容进行处理,所以在页面刷新的那一刻,所有的.paragraphs都会被渲染出来,看到的效果如上图。

事实上,现代浏览器往往更加智能。 基于这些场景,虽然我们非常希望能够对没有看到或滚动到的区域进行延迟加载。 只有当我们需要显示并滚动到那个地方时,页面内容才会被渲染。 。

基于这些场景,content-visibility: auto 应运而生。 它允许浏览器判断设置了该属性的元素。 如果该元素当前不在图层中,则不会渲染该元素。

基于上面的代码,我们只需要最小化并添加这样一段代码:

.paragraph {
    content-visibility: auto;
}

再看疗效,仔细观察两边的滚动条:

这里我使用::-webkit-scrollbar相关样式,让滚动条更加突出。

也许你还没有意识到发生了什么,我们来对比一下添加 content-visibility: auto 和不添加 content-visibility: auto 两种效果下文本的整体高度:

存在极其显着的差异。 这是因为设置了 content-visibility:auto 的元素当前未在不可见区域中渲染。 因此,虽然两侧内容的高度比正常状态切割要小很多。

好的,让我们实际开始滚动,看看会发生什么:

由于滚动过程中下面的元素只有出现在图层范围内才会渲染,所以滚动条有明显的犹豫晃动。 (其实这也是使用 content-visibility: auto 的小问题之一),但是可以明显看出移除css属性,这和我们一般使用 JavaScript 实现的延迟加载或者延迟加载非常相似。

事实上,与延迟加载不同的是,在向上滚动的过程中,上面消失的图层中已经渲染消失的元素也会因消失在图层中而再次被隐藏。 为此,即使页面滚动到底部,整体滚动条高度也不会改变。

内容可见性也能优化渲染性能吗?

那么,内容可见性也能优化渲染性能吗?

在Youtube--Slashinglayoutcostwithcontent-visibility[1]中,给出了一个特别好的反例。

这里我简单的重现一下。

对于含有大量HTML内容的页面,比如这个页面--HTML-LivingStandard[2]

你会感觉到,如果你向下滚动,你永远不会到达终点。 (这里我在本地模拟了页面,复制了页面的所有DOM,没有在网站上实际测试过)

如果你不对这个页面做任何事情,看一下第一次渲染所花费的时间:

可以看到,DOMContentLoaded耗时3s+,整整2900ms都花在了渲染上!

如果你给这个页面的每个段落添加 content-visibility:auto ,那么看看整体的持续时间:

可以看到,DOMContentLoaded的时间下降到了500ms+,Rendering花费的时间直接优化到了61ms!

2900ms-->61ms,这是一个惊人的优化水平。 为此,content-visibility:auto对于长文本和长列表功能的优化是显而易见的。

使用contain-intrinsic-size解决滚动条晃动问题

事实上,内容可见性也存在一些小问题。

从前面的例子中我们也可以看到,当使用content-visibility:auto处理长文本和长列表时。 滚动页面时滚动条仍然颤抖,体验不太好。

其实这也是很多虚拟列表都存在的问题。

幸运的是,标准制定者也发现了这个问题。 这里我们可以使用另一个CSS属性,也就是文章开头提到的另一个属性——contain-intrinsic-size,来解决这个问题。

contains-intrinsic-size:控制内容可见性指定的元素的自然大小。

你是什​​么意思?

或者里面的情况

<div class="g-wrap">
    <div class="paragraph">...</div>
    // ... 包含了 N 个 paragraph
    <div class="paragraph">...</div>
</div>

如果我们对视口之外的元素不使用contain-intrinsic-size而只使用content-visibility: auto,则层外元素的高度一般会为0。

其实如果直接给父元素设置固定高度的话,也会有一个高度。

对于这么实用的滚动效果,滚动条是晃动的:

因此,我们可以同时使用contain-intrinsic-size。 如果我们能够知道渲染状态下设置了 content-visibility:auto 的元素的高度,我们就可以填写相应的高度。 如果您确切知道高度,也可以填写一个近似值:

.paragraph {
    content-visibility: auto;
    contain-intrinsic-size320px;
}

此后,浏览器会给实际未渲染的层外的.paragraph元素赋予一个高度,以防止滚动条晃动:

你可以自己尝试一下:CodePenDemo--content-visibility:autoDemo[3]

内容可见性的一些其他问题