乐趣区

关于前端:使用-contentvisibility-优化渲染性能

最近在业务中理论应用 content-visibility 进了一些渲染性能的优化。

这是一个比拟新且有弱小性能的属性。本文将率领大家深刻了解一番。

何为 content-visibility

content-visibility:属性管制一个元素是否渲染其内容,它容许用户代理(浏览器)潜在地省略大量布局和渲染工作,直到须要它为止。

MDN 原文:The content-visibility CSS property controls whether or not an element renders its contents at all, along with forcing a strong set of containments, allowing user agents to potentially omit large swathes of layout and rendering work until it becomes needed. Basically it enables the user agent to skip an element’s rendering work (including layout and painting) until it is needed — which makes the initial page load much faster.

它有几个常见的取值。

/* Keyword values */
content-visibility: visible;
content-visibility: hidden;
content-visibility: auto;

别离解释一下:

  • content-visibility: visible:默认值,没有任何成果,相当于没有增加 content-visibility,元素的渲染与平常统一。
  • content-visibility: hidden:与 display: none 相似,用户代理将跳过其内容的渲染。(这里须要留神的是,跳过的是内容的渲染)
  • content-visibility: auto:如果该元素不在屏幕上,并且与用户无关,则不会渲染其后辈元素。

contain-intrinsic-size

当然,除 content-visibility 之外,还有一个与之配套的属性 — contain-intrinsic-size

contain-intrinsic-size:管制由 content-visibility 指定的元素的天然大小。

下面两个属性光看定义和介绍会有点绕。

咱们首先来看看 content-visibility 如何具体应用。

content-visibility: visible 是默认值,增加后没有任何成果,咱们就间接跳过。

利用 content-visibility: hidden 优化展现切换性能

首先来看看 content-visibility: hidden,它通常会拿来和 display: none 做比拟,然而其实它们之间还是有很大的不同的。

首先,假如咱们有两个 DIV 包裹框:

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

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

.g-wrap > div {
    width: 200px;
    height: 200px;
    background: #000;
}

成果如下:

OK,没有问题,接下来,咱们给其中的 .hidden 设置 content-visibility: hidden,看看会产生什么:

.hidden {content-visibility: hidden;}

成果如下:

留神,认真看成果,这里增加了 content-visibility: hidden 之后,隐没的只是增加了该元素的 div 的子元素隐没不见,而父元素自身及其款式,还是存在页面上的

如果咱们去掉设置了 content-visibility: hidden 的元素自身的 widthheightpaddingmargin 等属性,则元素看上去就如同设置了 display: none 个别,在页面上隐没不见了。

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

设置了 content-visibility: hidden 的元素,其元素的子元素将被暗藏,然而,它的渲染状态将会被缓存。所以,当 content-visibility: hidden 被移除时,用户代理无需重头开始渲染它和它的子元素。

因而,如果咱们将这个属性利用在一些一开始须要被暗藏,然而其后在页面的某一时刻须要被渲染,或者是一些须要被频繁切换显示、暗藏状态的元素上,其渲染效率将会有一个十分大的晋升。

利用 content-visibility: auto 实现虚构列表

OK,接下来是 content-visibility 的外围用法,利用 auto 属性值。

content-visibility: auto 的作用是,如果该元素不在屏幕上,并且与用户无关,则不会渲染其后辈元素。是不是与 LazyLoad 十分相似?

咱们来看这样一个 DEMO,理解其作用:

假如,咱们存在这样一个 HTML 构造,含有大量的文本内容:

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

每个 .paragraph 的内容如下:

因而,整个的页面看起来就是这样的:

因为,咱们没有对页面内容进行任何解决,因而,所有的 .paragraph 在页面刷新的一瞬间,都会进行渲染,看到的成果就如上所示。

当然,古代浏览器更加趋于智能,基于这种场景,其实咱们十分心愿对于仍未看到,仍旧未滚动到的区域,能够提早加载,只有到咱们须要展现、滚动到该处时,页面内容才进行渲染。

基于这种场景,content-visibility: auto 就应运而生了,它容许浏览器对于设置了该属性的元素进行判断,如果该元素以后不处于视口内,则不渲染该元素。

咱们基于上述的代码,只须要最小化,增加这样一段代码:

.paragraph {content-visibility: auto;}

再看看成果,仔细观察右侧的滚动条:

这里我应用了 ::-webkit-scrollbar 相干款式,让滚动条更显著。

可能你还没意识到产生了什么,咱们比照下增加了 content-visibility: auto 和没有增加 content-visibility: auto 的两种成果下文本的整体高度:

有着非常明显的差别,这是因为,设置了 content-visibility: auto 的元素,在非可视区域内,目前并没有被渲染,因而,右侧内容的高度其实是比失常状态下少了一大截的。

好,咱们理论开始进行滚动,看看会产生什么:

因为下方的元素在滚动的过程中,呈现在视口范畴内才被渲染,因而,滚动条呈现了显著的飘忽不定的抖动景象。(当然这也是应用了 content-visibility: auto 的一个小问题之一),不过显著能够看出,这与咱们通常应用 JavaScript 实现的虚构列表十分相似。

当然,在向下滚动的过程中,上方隐没的曾经被渲染过且隐没在视口的元素,也会因为隐没在视口中,从新被暗藏。因而,即使页面滚动到最下方,整体的滚动条高度还是没有什么变动的。

content-visibility 是否可能优化渲染性能?

那么,content-visibility 是否可能优化渲染性能呢?

在 Youtube — Slashing layout cost with content-visibility 中,给了一个十分好的例子。

这里我简略复现一下。

对于一个存在巨量 HTML 内容的页面,譬如相似于这个页面 — HTML – Living Standard

能够感触到,往下翻,基本翻不到止境。(这里我在本地模仿了该页面,复制了该页面的所有 DOM,并非理论在该网站进行测试)

如果不对这个页面做任何解决,看看首次渲染须要破费的工夫:

能够看到,DOMContentLoaded 的工夫的 3s+,而破费在 Rendering 上的就有整整 2900ms

而如果给这个页面的每个段落,增加上 content-visibility: auto,再看看整体的耗时:

能够看到,DOMContentLoaded 的工夫骤降至了 500ms+,而破费在 Rendering 上的,间接优化到了 61ms

2900ms –> 61ms,堪称是惊人级别的优化了。因而,content-visibility: auto 对于长文本、长列表性能的优化是不言而喻的。

利用 contain-intrinsic-size 解决滚动条抖动问题

当然,content-visibility 也存在一些小问题。

从下面的例子,也能看到,在利用 content-visibility: auto 解决长文本、长列表的时候。在滚动页面的过程中,滚动条始终在抖动,这不是一个很好的体验。

当然,这也是许多虚构列表都会存在的一些问题。

好在,标准制定者也发现了这个问题。这里咱们能够应用另外一个 CSS 属性,也就是文章一结尾提到的另外一个属性 — contain-intrinsic-size,来解决这个问题。

contain-intrinsic-size:管制由 content-visibility 指定的元素的天然大小。

什么意思呢?

还是下面的例子

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

如果咱们不应用 contain-intrinsic-size,只对视口之外的元素应用 content-visibility: auto,那么视口外的元素高度通常就为 0。

当然,如果间接给父元素设置固定的 height,也是会有高度的。

那么理论的滚动成果,滚动条就是抖动的:

所以,咱们能够同时利用上 contain-intrinsic-size,如果能精确晓得设置了 content-visibility: auto 的元素在渲染状态下的高度,就填写对应的高度。如果如法精确晓得高度,也能够填写一个大略的值:

.paragraph {
    content-visibility: auto;
    contain-intrinsic-size: 320px;
}

如此之后,浏览器会给未被理论渲染的视口之外的 .paragraph 元素一个高度,避免出现滚动条抖动的景象:

你能够本人亲自尝试感受一下:CodePen Demo — content-visibility: auto Demo

content-visibility: auto VS LazyLoad

那么,content-visibility: auto 是否能够代替 LazyLoad(懒加载)呢?

咱们来看看咱们通常对于 LazyLoad(懒加载)的一个定义。

LazyLoad:通常而言,LazyLoad 的作用在于,当页面未滚动到相应区域,该区域内的资源(网络申请)不会被加载。反之,当页面滚动到相应区域,相干资源的申请才会被发动。

那么,如果 content-visibility: auto 要可能代替 LazyLoad,则须要做到,初始化渲染的时候,在页面以后展现范畴外的,设定了 content-visibility: auto 的元素内的一些动态资源不会被加载。

这里我尝试做了一个简略的 DEMO:

还是借助上述的代码,假如咱们有如下的 HTML 构造,也就是在上述代码根底上,插入一些图片资源:

<div class="g-wrap">
    <div class="paragraph">...</div>
    // ... 蕴含了 N 个 paragraph
    <div class="paragraph">...</div>
    <div class="g-img">
      <img src="https://www.womenly.net/wp-content/uploads/2017/03/Tips-to-Maintain-the-Soft-Skin.jpg">
    </div>
    <div class="g-img">
      <img src="https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcTD8kEsEE3hJ64aU-_TKQJtvKDtTOGQfT3A4A&usqp=CAU">
    </div>
    <div class="g-img">
      <img src="https://i.pinimg.com/originals/e8/ba/25/e8ba252917952f23dfc9715e942e654e.jpg">
    </div>
</div>

相应设置下 CSS:

.paragraph,
.g-img {content-visibility: auto;}

当刷新页面的时候,察看网络申请(Network)的情况:

即使以后页面可视区域外的内容未被渲染,然而图片仍然会被加载!

因而,这也失去了一个十分重要的论断:

content-visibility: auto 无奈间接代替 LazyLoad,设置了 content-visibility: auto 的元素在可视区外只是未被渲染,然而其中的动态资源仍旧会在页面初始化的时候被全副加载

所以,在理论应用中,如果你的业务中曾经应用了比较完善的 Lazyload 解决长列表或者一些图片资源,那么 content-visibility: auto 不是更好的抉择。

可拜访性功能探索

当然,content-visibility: auto 的个性又引申出了另外一个有意思的点。

如果说可视区外的内容未被渲染,那是否会影响用户进行全文检索呢?毕竟这是一个十分重要的性能。

咱们再来做个探索,还是下面的 DEMO,咱们在首尾增加两个非凡的字符串:

<div class="g-wrap">
    <div class="text">
        <p>content-visibility: auto 对搜寻性能影响的探索 </p>
    </div>
    <div class="paragraph">...</div>
    // ... 蕴含了 N 个 paragraph
    <div class="paragraph">...</div>
    <div class="text">
        <p>content-visibility: auto 对搜寻性能影响的探索 </p>
    </div>
</div>

相应设置下 CSS:

.paragraph,
.text {content-visibility: auto;}

好,如此一来,在页面刷新后,第二个 .text 是处于未被渲染状态,咱们试着全局 ctrl + F 查找一下,看看能找到几个:

很有意思的景象,全局查找的时候,能够找到以后未被渲染的元素内的内容。

这里,咱们能够失去另外一个十分重要的点:

即使存在设置了 content-visibility: auto 的未被渲染的元素,然而它并不会影响全局的搜寻性能

这也是 content-visibility 设计上充沛的思考,对 可拜访性 性能,或者说用户体验的考量,有了这一点,对于它的理论应用有着十分大的帮忙。

content-visibility 的一些其余问题

首先,看看 content-visibility 的兼容性(2022-06-03):

目前还是比拟惨淡的,并且我没有理论在业务中应用它,须要再期待一段时间。当然,因为该属性属于渐进加强一类的性能,即使​生效,也齐全不影响页面自身的展现。

同时,也有一些同学示意,利用 content-visibility: auto 只能解决局部场景,在海量 DOM 的场景下的实际效果,还有待进一步的实测。真正使用的时候,多做比照,在做取舍。

当然,古代浏览器曾经越来越智能,相似 content-visibility 性能的属性也越来越多,咱们在性能优化的路上有了更多抉择,总归是一件坏事。

总结一下

再简略总结一下:

  1. 在一些须要被频繁切换显示、暗藏状态的元素上,应用 content-visibility: hidden,用户代理无需重头开始渲染它和它的子元素,能无效的晋升切换时的渲染性能;
  2. content-visibility: auto 的作用更加相似于虚构列表,应用它能极大的晋升长列表、长文本页面的渲染性能;
  3. 正当应用 contain-intrinsic-size 预估设置了content-visibility: auto 元素的高宽,能够无效的防止滚动条在滚动过程中的抖动;
  4. content-visibility: auto 无奈间接代替 LazyLoad,设置了 content-visibility: auto 的元素在可视区外只是未被渲染,然而其中的动态资源仍旧会在页面初始化的时候被全副加载;
  5. 即使存在设置了 content-visibility: auto 的未被渲染的元素,然而它并不会影响全局的搜寻性能。

最初

本文到此结束,心愿对你有帮忙 :)

想 Get 到最有意思的 CSS 资讯,千万不要错过我的公众号 — iCSS 前端趣闻 😄

更多精彩 CSS 技术文章汇总在我的 Github — iCSS,继续更新,欢送点个 star 订阅珍藏。

如果还有什么疑难或者倡议,能够多多交换,原创文章,文笔无限,满腹经纶,文中若有不正之处,万望告知。

退出移动版