在我们项目实际运行中,浏览器在接收到服务端返回的 HTML 之后,需要把这段数据渲染成用户看到的页面,在开始渲染第一个元素之前可能还需要经过很多步骤。这个过程会适用于整个页面,包括当前不可见的内容。所以在首屏渲染的时候很有可能加载了很多用户第一时间看不到的内容,这就可能会导致性能问题。现在我们可以无需额外的插件引入,直接用原生的CSS去解决这个问题。这就是我们今天的主角content-visibility
属性定义
content-visibility可以控制一个元素是否渲染其内容,它可以在某些时候省去大量布局和渲染工作,直到需要它为止。
它有visible,hidden,auto几个属性。
content-visibility: visible:默认值,没有任何效果,相当于没有添加 content-visibility,元素的渲染与往常一致。
content-visibility: hidden:与 display: none 类似,用户代理将跳过其内容的渲染。
content-visibility: auto:如果该元素不在屏幕上,并且与用户无关,则不会渲染其后代元素。
这里我们主要用到的就是content-visibility: auto,它可以立即提高我们页面性能,一起来看看下面的案例。
content-visibility的应用场景
content-visibility: auto看它的定义和懒加载LazyLoad 非常相似,我们很容易联想到它的使用场景。
以下是一个滚动列表的加载动作,我们改动了一下浏览器进度条,让动作看起来更明显。
<div class="box">
</div>
<style>
::-webkit-scrollbar {
width: 16px;
}
::-webkit-scrollbar-track {
background-color: #e4e4e4;
border-radius: 100px;
}
::-webkit-scrollbar-thumb {
background-color: #d4aa70;
border-radius: 100px;
}
.box {
width: 600px;
margin: 0 auto;
}
.paragraph {
font-size: 18px;
content-visibility: auto;
}
.paragraph>img {
width: 400px;
}
</style>
<script>
const dom = document.querySelector('.box')
for (let index = 0; index <= 50; index++) {
dom.innerHTML += ` <div class="paragraph">
<p>测试文案测试文案测试文案测试文案测试文案测试文案测试文案测试文案测试文案测试文案测试文案</p>
<img src="https://gw.alipayobjects.com/zos/antfincdn/aPkFc8Sj7n/method-draw-image.svg" alt="" />
</div>`
}
</script>
如上所示,我们在列表每个段落上设置content-visibility: auto; 可以观察到随着页面到滚动,浏览器的滚动条也在逐渐的变窄,并且伴随着抽动,这就说明实际的渲染列表在加载变长。但是这种抽动的滚动条在体验上不好,我们还是希望滚动条是丝滑流畅的。这里就要用到contain-intrinsic-size来优化体验了。
contain-intrinsic-size是定义了元素受尺寸局限时浏览器用于布局的元素尺寸,他有两个参数,第一个参数是宽,第二个参数是高,该值也可以简写合并成一个值。
这里我们来尝试设置一下
.paragraph {
font-size: 18px;
content-visibility: auto;
contain-intrinsic-size: 400px;
}
我们设置完之后可以发现,快速滑动时滚动条的抽动大幅度改善。原理其实就是给列表没有渲染的元素一个占位符,这样就可以减少渲染前后的高度差,从而改善滚动效果。
content-visibility的优势和局限
那么我们写了这么多,具体量化效果是怎么样的呢,这边是渲染500条数据的对比
无设置
设置了content-visibility:auto
可以看到对渲染效率约有4倍提升,当然这个效果在不同机器和不同平台和不同运行状态之间可能会有差异,但是content-visibility:auto带来的渲染性能提升还是很可观的。
但是content-visibility:auto并不是真正的懒加载,准确的说它的懒渲染,对于页面的图片等静态资源还是会进行下载,所以当页面有图片等静态资源的时候可以配合 loading="lazy"等懒加载方式配合实现性能优化。
此外没有被加载的节点并没有消失,我们仍然可以正常访问, 用浏览器搜索就可以验证这一点。这也增加了实际使用中的灵活性。