专栏名称: 前端早读课
我们关注前端,产品体验设计,更关注前端同行的成长。 每天清晨五点早读,四万+同行相伴成长。
目录
相关文章推荐
前端大全  ·  10分钟速成:轻松搭建前端monorepo架 ... ·  昨天  
前端之巅  ·  iOS 上手 Vision Pro ... ·  3 天前  
前端大全  ·  Mitosis:跨框架的UI组件解决方案 ·  3 天前  
奇舞精选  ·  Chrome 129:正式推出原生的 ... ·  1 周前  
51好读  ›  专栏  ›  前端早读课

【第3393期】使用 CSS content-visibility 提高渲染性能

前端早读课  · 公众号  · 前端  · 2024-10-16 08:00

正文

前言

介绍了如何使用 CSS 的 content-visibility 属性来提高网页在渲染大量元素时的性能。今日前端早读课文章由 @ikoofe 翻译,公号:KooFE 前端团队授权分享。

正文从这开始~~

最近,我在 emoji-picker-element 上遇到了一个性能问题:在一个接近 20k 个自定义表情符号的 Fediverse 实例上,打开表情符号选择器时,页面至少冻结了一秒钟,之后会卡顿一段时间。

【第2073期】content-visibility: 一个可以提高渲染性能的css属性

与 Slack、Discord 等类似,在 Mastodon 或 Fediverse 的不同服务器上也可以有自己的自定义表情符号。对于 20k 大小的自定义表情符号虽然不常见,但也并非不会出现。

在启动了他们的 Repro 之后,它确实很慢:

这里存在的问题是:

  • 20k 自定义表情符号意味着 40k 元素,因为每个元素都使用了  和 

  • 由于没有使用虚拟化,这些元素全部都被塞进了 DOM 中

让人欣慰的是,在这里使用了 ,所以那 20k 张图片并不是一次全部下载的。但无论如何,渲染 40k 元素都会非常缓慢 - Lighthouse 的建议是不要超过 1,400 个!

当然,我的第一个想法是,“谁会有 20k 的自定义表情符号?”,我的第二个想法是,“唉,我想我应该做虚拟化”。

【早阅】用 React 虚拟化优化性能:行业级解决方案

当初,我刻意避免了 emoji-picker-element 中的虚拟化,即因为:1) 它很复杂,2) 我认为我不需要它,以及 3) 它会影响可访问性。

我之前有过做虚拟列表的经验:在另外一个类库 Pinafore 上实现一个很大的虚拟列表。其中,使用了 ARIA: feed role,我做了所有计算,并添加了一个选项来禁用 “无限滚动”,因为有些人不喜欢它。为了尽快解决这个卡顿问题,我开始打算实现类似的虚拟列表。

然而,几天后,我脑海中突然冒出一个想法:为什么不试试 CSS content-visibility 呢?从上面的性能监控截图中可以看到,在布局和绘画上花费了大量时间。因此,使用 content-visibility 可能是一个比全面虚拟化简单得多的解决方案。

content-visibility 是一个全新的 CSS 功能,它允许开发者从布局和绘制的角度 “隐藏” DOM 的某些部分。它在很大程度上不会影响可访问性(因为 DOM 节点仍然存在),它不会影响在页面中查找 (⌘+F/Ctrl+F),并且不需要虚拟化。它所需要的只是对屏幕外的元素进行调整,以便浏览器可以在其中保留空间。

在这里,可以把 emoji 的分类作为要调整的元素单元,比如 Fediverse 的自定义表情有 blobs、cats 等分类。

幸运的是,我有一个很好的 atomic 单元来调整大小:emoji 类别。Fediverse 上的自定义表情符号往往分为一口大小的类别:“blobs”、“cats” 等:

对于每个分类,表情符号的大小以及行数和列数是已知的,因此可以使用 CSS 自定义属性来计算出预期的大小:

 .category {
content-visibility: auto;
contain-intrinsic-size:
/* width */
calc(var(--num-columns) * var(--total-emoji-size))
/* height */
calc(var(--num-rows) * var(--total-emoji-size));
}

这些占位符占用的空间与最终展现的空间完全相同,因此在滚动时不会有任何跳动。

接下来,我们来验证一下效果。对于初始加载,在 Chrome 中大约提升了 15%,在 Firefox 中提升了 5%。与虚拟列表相比,这个优化的效果并不能令人满意,因为虚拟列表可以做得更好!

【第3331期】爱彼迎以用户体验驱动的 Web 性能度量

我们继续看性能监控图。其中布局成本几乎没有了,但还有其他我无法解释的成本。例如,Chrome 跟踪中的这个大的无差别 blob 是怎么回事?

很显然, Chrome 对我们 “隐藏” 了一些性能信息时,我们可以这么做:取消 chrome:tracing,或者(最近)在 DevTools 中启用实验性的 “显示所有事件” 选项。这样 Chrome 会为我们提供更多的性能监控信息。设置好之后,截图如下:

这里出现了 ResourceFetcher::requestResource 提示信息。虽然我并不知道它是做什么的,但猜测是和  有关系。把所有  中的 src 中注释掉 - 这个神秘的成本就消失了!所以 loading="lazy" 也并不是免费午餐。这里最直接的做法是把 loading="lazy"

与其是去掉 loading="lazy",还不如直接把  移除掉,这样还能减少 DOM 元素属性,从 40k 个 DOM 元素减少到 20k。

我们可以使用 CSS 给  设置一个 ::after 伪元素,将图片作为它的背景来展示出来:

 .onscreen .custom-emoji::after {
background-image: var(--custom-emoji-background);
}

此时,当视图滚动到某个分类时,只需通过 IntersectionObserver 来添加 .onscreen。它的性能提升得更多。在基准测试中, Chrome 和 Firefox 中都实现了~45% 的改进,原始重现从~3 秒缩短到~1.3 秒。注意:由于跨浏览器的差异,Safari 的对 contentvisibilityautostatechange 的支持不够好,所以这里选择了 IntersectionObserver。

虽然,这个方案渲染 20k DOM 节点永远不会像虚拟化列表那样快。另外,一旦出现更多的表情符号,这个方案也无法扩展。但是 content-visibility 的实现成本比较低,我们根本不需要改变 ARIA 策略,也不用担心在页面中查找。但是如果追求更高的性能,虚拟列表仍然是最好的选择

关于本文
译者:@ikoofe
译文:https://mp.weixin.qq.com/s/iPFQEN2MgtzIfzXpW7J-cQ
作者:@Nolan
原文:
https://nolanlawson.com/2024/09/18/improving-rendering-performance-with-css-content-visibility/

这期前端早读课
对你有帮助,帮” 
 “一下,
期待下一期,帮”
 在看” 一下 。