找回密码
 会员注册
查看: 19|回复: 0

使用CSScontent-visibility提高渲染性能

[复制链接]

2

主题

0

回帖

7

积分

新手上路

积分
7
发表于 2024-10-10 10:12:26 | 显示全部楼层 |阅读模式
使用 CSS content-visibility 提高渲染性能 ikoofe KooFE前端团队 KooFE前端团队 关注前端最新动态 89篇内容 2024年10月02日 06:06 北京 本文翻译自 Improving rendering performance with CSS content-visibility,点击底部链接查看原文。最近,我在 emoji-picker-element 上遇到了一个性能问题:在一个接近 20k 个自定义表情符号的 Fediverse 实例上,打开表情符号选择器时,页面至少冻结了一秒钟,之后会卡顿一段时间。与 Slack、Discord 等类似,在 Mastodon 或 Fediverse 的不同服务器上也可以有自己的自定义表情符号。对于 20k 大小的自定义表情符号虽然不常见,但也并非不会出现。在启动了他们的 Repro 之后,它确实很慢:这里存在的问题是:20k 自定义表情符号意味着 40k 元素,因为每个元素都使用了和由于没有使用虚拟化,这些元素全部都被塞进了 DOM 中让人欣慰的是,在这里使用了,所以那 20k 张图片并不是一次全部下载的。但无论如何,渲染 40k 元素都会非常缓慢 - Lighthouse 的建议是不要超过 1,400 个!当然,我的第一个想法是,“谁会有 20k 的自定义表情符号?”,我的第二个想法是,“唉,我想我应该做虚拟化”。当初,我刻意避免了 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 自定义属性来计算出预期的大小: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%。与虚拟列表相比,这个优化的效果并不能令人满意,因为虚拟列表可以做得更好!我们继续看性能监控图。其中布局成本几乎没有了,但还有其他我无法解释的成本。例如,Chrome 跟踪中的这个大的无差别 blob 是怎么回事?很显然, Chrome 对我们“隐藏”了一些性能信息时,我们可以这么做:取消 chrome:tracing,或者(最近)在 DevTools 中启用实验性的“显示所有事件”选项。这样 Chrome 会为我们提供更多的性能监控信息。设置好之后,截图如下:这里出现了 ResourceFetcher::requestResource 提示信息。虽然我并不知道它是做什么的,但猜测是和有关系。把所有中的 src 中注释掉 - 这个神秘的成本就消失了!所以 loading="lazy" 也并不是免费午餐。这里最直接的做法是把 loading="lazy"。与其是去掉 loading="lazy",还不如直接把移除掉,这样还能减少 DOM 元素属性,从 40k 个 DOM 元素减少到 20k。我们可以使用 CSS 给设置一个 ::after 伪元素,将图片作为它的背景来展示出来:css.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 策略,也不用担心在页面中查找。但是如果追求更高的性能,虚拟列表仍然是最好的选择
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 会员注册

本版积分规则

QQ|手机版|心飞设计-版权所有:微度网络信息技术服务中心 ( 鲁ICP备17032091号-12 )|网站地图

GMT+8, 2024-12-29 16:05 , Processed in 0.821458 second(s), 26 queries .

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表