哪些 CSS 选择器或规则可以显着影响现实世界中的前端布局/渲染性能?

Posted

技术标签:

【中文标题】哪些 CSS 选择器或规则可以显着影响现实世界中的前端布局/渲染性能?【英文标题】:Which CSS selectors or rules can significantly affect front-end layout / rendering performance in the real world? 【发布时间】:2012-08-30 01:28:02 【问题描述】:

是否值得担心 CSS 渲染性能? 或者我们是否应该完全不担心 CSS 的效率,而只专注于编写优雅或可维护的 CSS?

这个问题旨在为前端开发人员提供有用的资源,了解 CSS 的哪些部分实际上会对设备性能产生重大影响,以及哪些设备/浏览器或引擎可能会受到影响。这不是一个关于如何编写优雅或可维护的 CSS 的问题,它纯粹是关于性能的(尽管希望这里写的内容可以为更多关于最佳实践的一般文章提供信息)。

现有证据

Google 和 Mozilla 编写了关于编写高效 CSS 的指南,CSSLint's set of rules 包括:

避免使用看起来像正则表达式的选择器 .. 不要使用复杂的等式运算符来避免性能损失

但他们都没有提供任何证据(我能找到)这些影响。

css-tricks.com article on efficient CSS 认为(在概述了大量效率最佳实践之后)我们现在应该not .. sacrifice semantics or maintainability for efficient CSS

perfection kills blog post 建议 border-radiusbox-shadow 呈现比简单的 CSS 规则慢几个数量级。这在 Opera 引擎中非常重要,但在 Webkit 中微不足道。此外,smashing magazine CSS benchmark 发现 CSS3 显示规则的渲染时间微不足道,并且比使用图像渲染等效效果要快得多。

了解您的移动设备 tested various mobile browsers 并发现它们都以同样微不足道的速度渲染 CSS3(在 12 毫秒内),但看起来他们在 PC 上进行了测试,因此我们无法推断手持设备的性能如何一般的 CSS3。

网上有aremanyarticles关于如何编写高效的CSS。但是,我还没有找到任何全面的证据表明 CSS 实际上对网站的呈现时间或快速性有显着的影响。

背景

我为这个问题提供了悬赏,试图利用 SO 的社区力量来创建一个有用的、经过充分研究的资源。

【问题讨论】:

我可以肯定地告诉你一件事:在应该使用 ID 时使用 ID,在应该使用类时使用类。性能差异可以忽略不计,语义则不然。元素的 ID——根据定义——只出现一次;那些可以在整个页面中重复的课程。只需考虑将类用于fixed CSS 位置的极端情况。 @MichałGórny ID 应该在 markup 中适当的地方使用,但是很多人(包括我自己)认为 ID 永远不应该在 CSS 选择器中使用。阅读这篇文章以获得(希望是公正的)详细说明:screwlewse.com/2010/07/dont-use-id-selectors-in-css 嗯,那篇文章同意我关于何时可以并且应该使用 ID 的观点。我的极端例子position: fixed 是一个根本不应该重用CSS 的例子。并不是说我提倡做这样的事情。 请记住,大多数浏览器已经尽力优化选择器。以right-to-left matching on a per-element basis 为例。大多数选择器的速度与页面上的元素一样慢。如果您有一个非常简单的页面,其中只有三个 body 的子级而没有其他任何内容,那么您向其抛出的任何选择器都不会导致浏览器出现故障甚至冻结。 @BoltClock 我对可以显着影响渲染性能的所有样式元素感兴趣。尽管选择器更容易为其定义最佳实践,但这些技巧可能最具有吸引力。 【参考方案1】:

首先想到的是:您使用的rendering engine 有多聪明?

这听起来很笼统,但在质疑 CSS 渲染/选择的效率时非常重要。例如,假设您的 CSS 文件中的第一条规则是:

.class1 
    /*make elements with "class1" look fancy*/

因此,当一个非常基本的引擎看到这一点时(因为这是第一条规则),它会查看 DOM 中的每个元素,并检查每个元素中是否存在 class1。更好的引擎可能会将类名映射到 DOM 元素列表,并使用哈希表之类的东西进行高效查找。

.class1.class2 
    /*make elements with both "class1" and "class2" look extra fancy*/

我们的示例“基本引擎”将重新访问 DOM 中的每个元素以查找这两个类。更聪明的引擎会比较n('class1')n('class2'),其中n(str) 是DOM 中的元素数与str 类,取最小值;假设那是class1,然后传递所有带有class1 的元素,寻找也有class2 的元素。

无论如何,现代引擎都很聪明(比上面讨论的例子更聪明),闪亮的新处理器每秒可以执行数百万(数千万)次操作。您的 DOM 中不太可能有数百万个元素,因此任何选择 (O(n)) 的最坏情况下的性能无论如何都不会太差。


更新:

为了获得一些实际的说明性证明,我决定做一些测试。首先,为了了解我们在实际应用程序中平均可以看到多少个 DOM 元素,我们来看看一些流行网站的网页有多少个元素:

Facebook:~1900 个元素(在我的个人主页上测试)。Google:~340 个元素(在主页上测试,没有搜索结果) .Google:~950 个元素(在搜索结果页面上测试)。Yahoo!:~1400 个元素(在主页上测试)。***: ~680 个元素(在问题页面上测试)。AOL::~1060 个元素(在主页上测试)。***:~6000 个元素,其中 2420 个不是 spansanchors(已在 Wikipedia article about Glee 上测试)。Twitter: ~270 个元素(已测试在主页上)。

将这些加起来,我们平均得到约 1500 个元素。现在是时候进行一些测试了。对于每个测试,我生成了 1500 个 divs(对于某些测试,嵌套在其他一些 divs 中),每个测试都有适当的属性,具体取决于测试。


测试

样式和元素都是使用 php 生成的。我已经上传了我使用的PHP,并创建了一个索引,以便其他人可以在本地测试:little link。


结果:

每个测试在三种浏览器上执行 5 次(报告平均时间):Firefox 15.0 (A)、Chrome 19.0.1084.1 (B)、Internet Explorer 8 (C):

                                                                        A      B      C
1500 class selectors (.classname)                                      35ms   100ms  35ms
1500 class selectors, more specific (div.classname)                    36ms   110ms  37ms
1500 class selectors, even more specific (div div.classname)           40ms   115ms  40ms
1500 id selectors (#id)                                                35ms   99ms   35ms
1500 id selectors, more specific (div#id)                              35ms   105ms  38ms
1500 id selectors, even more specific (div div#id)                     40ms   110ms  39ms
1500 class selectors, with attribute (.class[title="ttl"])             45ms   400ms  2000ms
1500 class selectors, more complex attribute (.class[title~="ttl"])    45ms   1050ms 2200ms

类似实验:

显然其他人也进行过类似的实验;这个也有一些有用的统计数据:little link。


底线:

除非您关心在渲染时节省几毫秒(1ms = 0.001s),否则不要过多考虑这个问题。另一方面,避免使用复杂的选择器来选择大型元素子集是一种很好的做法,因为这会产生一些明显的差异(正如我们从上面的测试结果中看到的那样)。在现代浏览器中,所有常见的 CSS 选择器都相当快。

假设您正在构建一个聊天页面,并且您想要设置所有消息的样式。你知道每条消息都在一个div 中,它有一个title,并嵌套在一个div 中,类.chatpage。使用.chatpage div[title] 选择消息是正确,但在效率方面也是不好的做法。给所有消息一个类并使用该类选择它们会更简单、更易于维护和更有效。


花哨的单行结论:

在“是的,这个 CSS 有意义”范围内的任何事情都是好的

【讨论】:

恐怕我希望得到比这更深入的答案(如果我没有得到很好的答案,我可能会在几天内添加赏金) .显然这取决于渲染引擎,但毫无疑问,我对最新版本的 Webkit (Chrome / Safari)、Gecko (Firefox) 和 Trident (IE)(以及在较小程度上,Presto)的性能特别感兴趣。至于您的一般观点,即渲染性能无关紧要,您确定这适用于复杂的 CSS3 查询,例如我的问题中提到的类似正则表达式的查询吗? @RobinWinslow 不是说没关系;您只是无法通过更改次要内容(例如“避免使用 ID”)来优化它。正则表达式并不像你所暗示的那么邪恶——同样,不要忘记你正在处理的字符串长度几乎不超过 10 个字符。 另一方面,尽可能避免使用更复杂的选择器可以为您提供:A) 更简洁的 CSS 文件。 B) 性能提升。如果 ID 确实像某些文章声称的那样糟糕,那么 CSS 规范就不会包含它们。 @Abody 我真的不想讨论“你应该使用 ID”的事情,因为它离题了,但你不能肯定地暗示 CSS 规范是完美的吗?作为对 A) 的回应,是的,它使 CSS 更清晰(这很好),但我再次特别询问性能影响。我仍然欢迎人们实际测量渲染性能的一些具体示例。 @SeanDunwoody 是的。我想这里的重点是,大多数常见的 CSS 选择器在所有浏览器中都足够快(100 毫秒也不算太糟糕),只要你不做不情愿的幻想,比如使用复杂的选择器来选择大量元素。最重要的是您的 CSS“有意义”。如果您正在构建一个聊天页面,并且您想要设置消息样式,并且所有消息divs 都有一个title。可以这样做:.chatpage div[title],但最好只给所有消息一个类,然后使用.message 设置它们的样式。它更简单、更易于维护、更高效。 刚刚偶然发现这一点,我发现这些测试很奇怪。为什么你认为这种计时方法实际上测量的是你想要测量的东西?仅仅因为脚本在 中并且就在文档末尾之前并不一定意味着 CSS 布局处理发生在它们之间。我猜这些数字有点奇怪的原因是至少 Firefox 只是独立于 CSS 布局执行脚本。这可以解释为什么它得到几乎恒定的结果。可靠地测量“视觉上完整”页面所需的时间可能会很困难。【参考方案2】:

这里的大多数答案都集中在选择器性能上,好像它是唯一重要的事情。我将尝试介绍一些精灵琐事(剧透警告:它们并不总是一个好主意)、css used value performance 和某些属性的渲染。

在我得到答案之前,让我先介绍一下 IMO:就个人而言,我强烈不同意对“基于证据的数据”的声明需求。它只是使性能声明看起来可信,而实际上,渲染引擎领域的异构性足以使任何此类统计结论无法衡量并且无法采用或监控。

随着最初的发现很快过时,我宁愿看到前端开发人员了解基础原则及其相对于可维护性/可读性布朗尼点的价值 - 毕竟,过早的优化是万恶之源 ;)


让我们从选择器性能开始:

Shallow, preferably one-level, specific selectors are processed faster. 原始答案中缺少显式性能指标,但关键点仍然存在:在运行时,html 文档被解析为包含平均深度为 DN 元素的 DOM 树,并且总共有S 已应用 CSS 规则。为了降低计算复杂度O(N*D*S),你应该

    让最右边的键匹配尽可能少的元素 - 选择器从右到左匹配^ 以获取单个规则资格,因此如果最右边的键不匹配特定的元素,不需要进一步处理选择器,它被丢弃。

    大家普遍认为应该避免* 选择器,但这一点应该更进一步。事实上,“正常”的 CSS 重置确实匹配大多数元素 - 当分析此 SO 页面时,重置负责所有选择器匹配时间的大约 1/3,因此您可能更喜欢 normalize.css(加起来只有 3.5ms - 反对过早优化的观点很重要

    避免使用后代选择器,因为它们最多需要迭代 ~D 元素。这主要影响不匹配确认 - 例如,一个正的 .container .content 匹配可能只需要一个步骤来处理父子关系中的元素,但是在负匹配之前,DOM 树需要一直遍历到 html确认。

    尽量减少 DOM 元素的数量,因为它们的样式是单独应用的(值得注意的是,这会被浏览器逻辑所抵消,例如引用缓存和从相同元素回收样式 - 对于例如,当设计相同的兄弟姐妹时)

    Remove unused rules 因为浏览器最终不得不评估它们对每个渲染元素的适用性。说得够多了 - 最快的规则就是不存在的规则 :)

从渲染引擎性能的角度来看,这些将导致可量化的(但取决于页面,不一定是可感知的)改进,但是总会有额外的因素,例如流量开销和 DOM 解析等。


接下来,CSS3属性表现:

CSS3 (除其他外)为我们带来了圆角、背景渐变和阴影变化——随之而来的是一大堆问题。想一想,根据定义,预渲染图像的性能要优于必须先渲染的一组 CSS3 规则。来自webkit wiki:

只能使用 CSS 中的渐变、阴影和其他装饰 必要时(例如,当形状根据内容动态时) - 否则,静态图像总是更快。

如果这还不够糟糕,则可能必须在每次重绘/重排事件时重新计算渐变等(更多详细信息见下文)。请记住这一点,直到大多数用户用户可以浏览 a css3-heavy page like this 而没有明显的延迟。


接下来,精灵表演:

避免使用又高又宽的精灵,即使它们的流量足迹相对较小。人们通常会忘记渲染引擎不能与 gif/jpg/png 一起使用,并且在运行时所有图形资源都作为未压缩的位图进行操作。至少很容易计算:this sprite 的宽度乘以高度乘以每像素四个字节 (RGBA) 为238*1073*4≅1MB。在同时打开的不同选项卡中的几个元素上使用它,它很快就会产生显着的价值。

一个相当极端的例子是been picked up on mozilla webdev,但是当使用像diagonal sprites 这样的可疑做法时,这一点也不意外。

可以考虑的替代方法是将单独的 base64 编码图像直接嵌入到 CSS 中。


接下来,重排和重绘:

认为reflow 只能通过 JS DOM 操作触发是一种误解——事实上,任何影响布局样式的应用程序都会触发它影响目标元素、其子元素和跟随它的元素等。唯一的方法防止不必要的迭代是尝试避免渲染依赖。一个简单的例子是rendering tables:

在完全建立布局之前,表格通常需要多次传递,因为它们是元素可以使用的罕见情况之一 影响 DOM 上出现在它们之前的其他元素的显示。 想象一下表格末尾的一个单元格,其内容非常广泛, 导致列完全调整大小。这就是为什么没有桌子 在所有浏览器中逐步呈现。


如果我回忆起遗漏的重要内容,我会进行编辑。一些链接完成:

http://perfectionkills.com/profiling-css-for-fun-and-profit-optimization-notes/

http://jacwright.com/476/runtime-performance-with-css3-vs-images/

https://developers.google.com/speed/docs/best-practices/payload

https://trac.webkit.org/wiki/QtWebKitGraphics

https://blog.mozilla.org/webdev/2009/06/22/use-sprites-wisely/

http://dev.opera.com/articles/view/efficient-javascript/

【讨论】:

我当然同意“渲染引擎领域是异构的”,但这似乎没有任何理由不提供统计数据。前端开发人员如何在不了解某些统计数据的情况下确定“基础原则”与“可维护性/可读性”的“相对价值”?只是因为领域是多样化的,而且总是在变化,这不是没有证据就采取行动的借口。 @RobinWinslow:您误解了我的回答 - 当原始处理逻辑可随时用于分析。例如,您想反对深度过度指定的选择器 - 您可以运行数百个测试,这些测试最终受浏览器及其版本、系统硬件、测试用例细节的影响......或者您可以阅读RTL 处理并看到无论浏览器引擎引入多少优化,此类选择器都会增加处理开销。 TL;DR:不要分析结果,要分析模型。无论如何,我确实警告过你这是一个 IMO ;)【参考方案3】:

虽然不直接与代码相关,但使用 <link> 而非 @import 包含样式表可提供更快的性能。

'Don’t use @import' via stevesouders.com

文章包含各种类型之间的大量速度测试示例,以及一种类型与另一种类型之间的速度测试示例(例如:通过 <link> 调用的 CSS 文件还包含 @import 到另一个 css 文件)。

【讨论】:

恐怕离题,也是我想大多数前端开发人员已经知道的更简单的性能调整之一。 也许,但不应该假设。涵盖基础知识绝不是一个坏主意。 除非它跑题了:p【参考方案4】:

对于这么大的赏金,我愿意冒着空回答的风险:没有官方的 CSS 选择器会导致渲染明显变慢,并且(在当今快速计算机和快速浏览器迭代的时代)任何浏览器厂商很快就解决了。即使在移动浏览器中也没有问题,除非粗心的开发人员愿意使用非标准的 jQuery 选择器。这些被 jQuery 开发人员标记为有风险,并且确实可能​​存在问题。

在这种情况下,缺乏证据就是缺乏问题的证据。因此,请使用语义标记(尤其是 OOCSS),并报告您在晦涩的浏览器中使用标准 CSS 选择器时发现的任何减速。

来自未来的人们:2012 年的 CSS 性能问题已经成为过去。

【讨论】:

【参考方案5】:

不是 css 是一种无关紧要的方式来让它更快,它一定是你在看性能时最不看的东西。以适合您的方式制作您的 css,编译它。然后把它放在头上。这可能很粗糙,但是当您查看浏览器性能时,它们还有很多其他需要寻找的东西。如果您在数字局工作,您不会因为加载时间增加 1 毫秒而获得报酬。

正如我评论的那样,使用 pagespeed for chrome 它是一个谷歌工具,可以分析 27 个参数 css 中的网站是其中之一。

我的帖子只是关注,不想让大约 99% 的网络用户能够打开网站并正确查看它,即使是使用 IE7 等的人。比使用 css3 关闭 10% (如果事实证明您可以获得额外的 1-10 毫秒的性能)。

大多数人至少有 1mbit/512kbit 或更高,如果你加载一个沉重的网站,加载大约需要 3 秒,但你可以在 css 上节省 10 毫秒??

当涉及到移动设备时,您应该为移动设备制作网站,因此当您的设备屏幕尺寸小于“宽度”像素时,您就有了一个单独的网站

请在下面评论这是我的观点和我对网络开发的个人经验

【讨论】:

这些性能实践众所周知并被接受。这个问题是关于rendering 性能的。鉴于渲染问题不如传输问题重要,我试图找出渲染性能的重要性以及在重要之前必须获得多复杂的选择器或渲染规则。感谢您在“这根本不重要”方面添加您的声音,但除此之外,这个答案实际上并没有对辩论做出贡献。 它的贡献在于所有设备都加快了渲染过程,除非您使用 150dpi 或更高的图片,否则无法真正研究它,这与 Web 无关显示为 72dpi。我可能会补充一点,如果你可以在浏览器中渲染 3d,那么 2d 比关心要快得多。但希望你能找到一些证明它明显更快,我在这件事上添加为最爱 好的,所以您的 150dpi 评论正是我正在寻找的那种东西 - 但我想要 证据 而不仅仅是您的断言:150dpi 有所作为的证据和证据其他渲染问题不是问题。我个人认为肯定有一些网站的设计如此复杂,以至于渲染 CSS 至少有点慢,至少在移动设备上是这样。 我知道你要去哪里,但它在网络上仍然只有 72dpi,但要渲染 150,你必须渲染两倍的像素量。如果将下载速度添加到渲染时间,您可能会遇到这样的情况,例如,如果您使用 css 3 或 css 2 制作圆角,那么与仅渲染相比,您有下载时间 + 渲染时间。【参考方案6】:

这是真的

10 年前计算机的速度要慢得多。

如今,您还拥有更多种类的设备可以访问您的网站。虽然台式机/笔记本电脑的发展突飞猛进,但中低端智能手机市场的设备在许多情况下并不比十年前的台式机强大多少。

但话虽如此,在为尽可能广泛的设备范围提供良好体验方面,CSS 选择速度可能是您需要担心的事情列表的底部。

在此基础上,我无法找到与更现代的浏览器或移动设备相关的特定信息,这些信息与低效的 CSS 选择器有关,但我能够找到以下信息:

    http://www.stevesouders.com/blog/2009/03/10/performance-impact-of-css-selectors/

    现在已经过时了(IE8、Chrome 2),但在某些浏览器中建立各种选择器的效率方面做出了不错的尝试,并且还尝试量化 CSS 规则的数量如何影响页面呈现时间。

    http://www.thebrightlines.com/2010/07/28/css-performance-who-cares/

    再次过时(IE8、Chrome 6),但在效率低下的 CSS 选择器* * * * * * * * * background: #ff1; 中使用极端情况来确定性能下降。

【讨论】:

+1 提到了设备的激增,但虽然智能手机的功能不那么强大,但渲染引擎已经变得更加高效。我特别欢迎智能手机难以解决的渲染问题的具体示例。 我无法在网上找到任何移动浏览器在基于低效选择器的渲染中苦苦挣扎的示例,但确实设法找到了一些稍微过时的示例,人们试图将一些数字实际放入各种低效的 css选择器,我已经相应地更新了我的答案,希望你会发现它有用。 太棒了,这些正是我正在寻找的资源。这两篇文章的主要结论似乎是,即使你真的试图制作低效的查询,它也只会产生微不足道的差异,这正是我正在寻找的那种结论。如果我们能找到包括移动设备在内的任何测试,那仍然很棒。我打算暂时搁置这个问题,看看其他人能想出什么,但这绝对是最佳候选答案。

以上是关于哪些 CSS 选择器或规则可以显着影响现实世界中的前端布局/渲染性能?的主要内容,如果未能解决你的问题,请参考以下文章

错误:“body”之后的无效 CSS:预期的选择器或规则,在第 4 行是“”

无法定位元素:python 爬行中的 css 选择器或 xpath

现实世界中哪些地方用到了Java?

如何在 CSS 选择器或 jQuery 中处理 XML 命名空间?

CSS 到 JSON 解析器或转换器

css选择器都有哪些?各种选择器优先级大小顺序