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

55

值得关注CSS渲染性能吗?还是我们应该完全不担心CSS的效率,只专注于编写优雅或易于维护的CSS呢?

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

现有证据

GoogleMozilla已经编写了有关编写高效CSS的指南,CSSLint的一组规则包括:

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

但是我没有找到任何证据表明它们的影响。一篇关于高效CSS的css-tricks.com文章在概述了一系列效率最佳实践后,认为我们现在应该“不要为了高效CSS而牺牲语义或可维护性”。perfection kills博客文章建议使用border-radiusbox-shadow比简单的CSS规则渲染得慢得多。这在Opera引擎中非常重要,但在Webkit中无关紧要。此外,smashing magazine CSS基准测试发现,CSS3显示规则的渲染时间微不足道,并且比使用图像渲染等效效果要快得多。

了解你的手机测试了各种移动浏览器,发现它们都在12毫秒内同样快地渲染CSS3,但看起来他们是在PC上进行测试的,所以我们不能推断手持设备在一般情况下如何使用CSS3。

很多 文章在互联网上介绍如何编写高效的CSS。然而,我还没有找到任何全面的证据表明糟糕的CSS实际上对网站的渲染时间或响应速度有显著影响。

背景

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


那篇文章在我认为何时可以和应该使用ID方面与我达成了共识。而我的极端示例 position: fixed 就是一个CSS不应该被重复使用的例子。并不是说我主张做这样的事情。 - Michał Górny
请记住,大多数浏览器已经尽力优化选择器。以每个元素为基础的从右到左匹配是一个众所周知的例子。大多数选择器只有在页面上存在元素时才会变慢。如果您的页面非常简单,只有 body 的三个子元素和其他内容,则任何选择器都不应该导致浏览器崩溃甚至冻结。 - BoltClock
此外,这个问题仅涉及或主要涉及选择器,还是CSS的每个方面?你应该知道,CSS性能并不仅仅体现在选择器上。浏览器还需要考虑布局、定位、计算和绘制渐变、透明合成以及各种其他事情。*那才是渲染。选择器匹配不是渲染;它只是找出哪些元素以何种方式进行渲染的过程。 - BoltClock
1
@BoltClock 我对所有可以显著影响渲染性能的样式元素都很感兴趣。虽然选择器更容易定义最佳实践,所以这些技巧可能会更有影响力。 - Robin Winslow
这只是一个针对您的网站的基准测试工具,通常我使用它来优化我的网站。 Pagespeed(由Google提供):https://chrome.google.com/webstore/detail/gplegfbjlmmehdoakndmohflojccocli,浏览器会读取您在CSS中所做的每个断点,因此文件越紧凑,效果就越好。有很多可以完成此操作的工具,但我不知道哪些标签效果更好。 - Simon Dragsbæk
显示剩余6条评论
6个回答

49
这里首先需要考虑的是,你正在使用的渲染引擎有多聪明?
尽管听起来很普通,但这在质疑CSS渲染/选择的效率时非常重要。例如,假设你的CSS文件中的第一条规则是:
.class1 {
    /*make elements with "class1" look fancy*/
}

因此,当一个非常基本的引擎看到这个(因为这是第一个规则),它会查看您DOM中的每个元素,并检查每个元素中是否存在class1。更好的引擎可能会将类名映射到DOM元素列表,并使用类似哈希表的东西进行高效查找。
.class1.class2 {
    /*make elements with both "class1" and "class2" look extra fancy*/
}

"Our example 'basic engine' would search through each element in the DOM to find both classes. A more sophisticated engine would compare n('class1') and n('class2'), where n(str) represents the number of elements in the DOM with the class str, and select the minimum value. If class1 is the minimum, then it will look for all elements with class1 and search for elements that also have class2.

"However, modern engines are much more advanced than the example mentioned above, and new processors can perform millions (even tens of millions) of operations per second. It's highly unlikely that you have millions of elements in your DOM, so the worst-case performance for any selection (O(n)) won't be too bad."

更新:

为了获得一些实际的实证证据,我决定进行一些测试。首先,为了了解我们在真实应用中可以看到多少平均DOM元素,请看一下一些受欢迎的网站页面有多少元素:

Facebook:约1900个元素(在我的个人主页上测试)
Google:约340个元素(在主页上测试,没有搜索结果)
Google:约950个元素(在搜索结果页面上测试)
Yahoo!:约1400个元素(在主页上测试)
Stackoverflow:约680个元素(在问题页面上测试)
AOL:约1060个元素(在主页上测试)
Wikipedia:约6000个元素,其中2420个不是标签(在关于Glee的维基百科文章上测试)
Twitter:约270个元素(在主页上测试)

总结一下,我们得到了大约1500个元素的平均值。现在是时候进行一些测试了。对于每个测试,我生成了1500个divs(对于某些测试嵌套在其他divs中),每个divs具有与测试相关的适当属性。


测试

样式和元素都是使用PHP生成的。我已经上传了所使用的PHP文件,并创建了一个索引,以便其他人可以在本地进行测试:小链接


结果:

每个测试在三个浏览器上进行 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

类似的实验:

显然,其他人已经做过类似的实验;这个实验还有一些有用的统计数据:小链接


总之:

如果你不在意节省渲染时间(1毫秒=0.001秒),那么不要太费心。另一方面,避免使用复杂的选择器来选择大量的元素是一个好的习惯,因为这可能会产生一些明显的差异(就像我们从上面的测试结果中看到的那样)。所有常见的CSS选择器在现代浏览器中都相当快。

假设你正在构建一个聊天页面,你想为所有消息设置样式。你知道每条消息都在具有titlediv 中,并嵌套在一个带有类 .chatpagediv 中。使用.chatpage div[title] 来选择消息是正确的,但从效率角度来看,这也是不好的做法。将所有消息都赋予一个类并使用该类进行选择更简单、更易维护,也更有效率。


华丽的一句话结论:

只要在“是的,这个CSS有意义”的范围内,任何东西都是可以的


1
@RobinWinslow 不是说它不重要;你只是不能通过改变小的东西(如“避免使用 ID”)来进行优化。正则表达式并不像你所暗示的那样邪恶--再次强调,不要忘记你正在处理的字符串几乎从来不超过10个字符。另一方面,在可以使用更简单的选择器时避免使用更复杂的选择器会使你获得:A) 更清洁的 CSS 文件。 B) 性能提升。如果像一些文章声称的那样,ID 真的很糟糕,CSS 规范就不会包括它们了。 - Chris
@RobinWinslow 好的,我已经更新了答案并附上了实际测试结果。 - Chris
是的,我知道Chrome的JavaScript引擎几乎摧毁了其他所有引擎(可能除了IonMonkey)。我猜这对于像Facebook、Google等非常重度使用JavaScript的流行网站来说更加重要(和显著),因此他们可能有点忽视了CSS引擎。 - Sean
1
@SeanDunwoody 是的。我想这里的重点是,大多数常见的CSS选择器在所有浏览器中都足够快(100ms还不错),只要您不使用复杂选择器来选择大量元素。最重要的是,你的CSS“有意义”。如果你正在构建一个聊天页面,并且你想要为消息设置样式,并且所有的消息div都有一个title。你可以这样做:.chatpage div[title],但最好还是给所有的消息添加一个类,然后使用.message样式它们。这更简单,更可维护,也更高效。 - Chris
1
刚刚发现这个,我觉得那些测试有点奇怪。你为什么认为这种定时方法实际上能够测量到你想要测量的内容呢?仅仅因为脚本在<head>之间和文档末尾之前运行,并不意味着CSS布局处理就一定会在它们之间进行。我猜测这些数字看起来有点奇怪的原因是至少 Firefox 只是独立地执行脚本而不会考虑CSS布局。这可以解释为什么它会得到几乎恒定的结果。要可靠地测量页面加载直到“视觉完成”的时间可能会很困难。 - Simon Lehmann
显示剩余7条评论

13

这里大多数答案都关注选择器性能,好像它是唯一重要的事情。我会尝试讨论一些雪碧图细节(剧透警告:它们并不总是一个好主意),以及某些属性的CSS使用性能和渲染。

在回答问题之前,让我先说一下我的看法:个人而言,我强烈反对所谓“基于证据的数据”的需求。它仅仅是让性能声明看起来可信,而实际上,渲染引擎领域是异构的,使得任何这样的统计结论都无法准确衡量,并且难以采用或监测。

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


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

浅层、最好是一级的、具体的选择器处理速度更快。 原始答案中缺少明确的性能指标,但关键点仍然存在:在运行时,HTML文档被解析成一个DOM树,其中包含N个元素,平均深度为D,然后应用了总共S个CSS规则。为了降低计算复杂度O(N*D*S),您应该

  1. 尽量让最右边的键匹配尽可能少的元素 - 选择器是从右到左匹配^以确定单个规则是否符合条件,因此如果最右边的键不匹配特定元素,则无需进一步处理选择器并将其丢弃。

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

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

  3. 最小化DOM元素数量,因为它们的样式是逐个应用的(值得注意的是,这会被浏览器逻辑抵消,例如引用缓存和从相同元素中回收样式-例如,在为相同兄弟姐妹设置样式时

  4. 删除未使用的规则,因为浏览器最终必须评估它们对每个呈现的元素的适用性。足够说了-最快的规则就是不存在的规则 :)

这些将导致可量化的(但取决于页面,不一定可感知)渲染引擎性能改进,然而总会有其他因素,如流量开销和DOM解析等。

接下来,CSS3属性性能:

CSS3带给我们(除了其他东西)圆角、背景渐变和阴影变化等,以及一堆问题。想想看,根据定义,预渲染的图像比一组必须先被渲染的CSS3规则表现更好。来自webkit wiki的信息:

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

如果这还不够糟糕,渐变等可能需要在每次重绘/回流事件中重新计算(下面有更多详细信息)。请记住这一点,直到大多数用户可以浏览像这样的CSS3重页面而没有明显的延迟。


接下来,精灵图性能方面的问题:

避免使用过高或过宽的精灵图,即使它们的流量占用相对较小。常常被忽略的是,渲染引擎无法处理 gif/jpg/png 格式,在运行时,所有图形资源都会被作为未压缩的位图进行操作。至少可以轻松计算出:这个精灵图的宽度乘以高度乘以每像素四个字节(RGBA),约等于 238*1073*4≅1MB。如果在不同同时打开的标签页中使用它,很快就会累加到一个显著的值。

这已经成为 Mozilla Webdev 上的一个极端案例,但当使用类似 对角线精灵图 这样有争议的做法时,这并不出乎意料。

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


接下来,重排和重绘:

有一个误解认为只有通过 JS DOM 操作才能触发reflow - 实际上,任何布局相关的样式应用都会触发它,影响目标元素、其子元素以及后续元素等。唯一避免不必要的迭代的方法是尝试避免渲染依赖。这方面的一个简单例子就是渲染表格

表格通常需要多次处理才能完全建立布局,因为它们是罕见的情况之一,其中元素可以影响在它们之前出现的其他元素的显示。 想象一下,在表格末端有一个非常宽的内容单元格,导致该列被完全重新调整大小。这就是为什么不会在所有浏览器中逐步呈现表格的原因。


如果我想起有被遗漏的重要信息,我会进行修改。以下是一些链接:

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/


1
我完全同意“渲染引擎领域是异质的”,但这并不是没有统计数据的理由。如果没有一些统计数据,前端开发人员如何决定“基础原则”相对于“可维护性/可读性”的“相对价值”呢?仅仅因为该领域多样化且不断变化,并不意味着我们可以没有证据地行动。 - Robin Winslow
@RobinWinslow:你误解了我的回答 - 我并不支持在原始处理逻辑已经可以进行分析的情况下寻求基于证据的数据。例如,如果你想反对深度过度指定的选择器 - 你可以运行数百个测试,这些测试最终受到浏览器及其版本、系统硬件、测试用例特定性的影响...或者你可以阅读RTL处理的相关内容,并了解这样的选择器将会增加处理开销,无论浏览器引擎引入多少优化。 - Oleg
简而言之:不要分析结果,要分析模型。无论如何,我已经警告过你这是我的个人意见 ;) - Oleg

5

虽然确实如此

十年前,电脑速度要慢得多。

但是现在有更多种类的设备可以访问您的网站。虽然台式机/笔记本电脑已经取得了飞跃性进展,但是中低端智能手机市场中的设备,在很多情况下与我们十年前的台式机差不多。

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

就这个问题扩展一下,我无法找到关于现代浏览器或移动设备在处理效率低下的CSS选择器方面的具体信息,但我能够找到以下内容:

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

    现在相当过时(IE8,Chrome 2),但是尝试在某些浏览器中建立各种选择器的效率,并试图量化CSS规则数如何影响页面渲染时间。

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

    同样相当过时(IE8,Chrome 6),但在低效CSS选择器 * * * * * * * * * { background: #ff1; } 方面采取了极端措施,以确定性能下降。


1
虽然提到了设备的普及,但是智能手机的性能较弱,而渲染引擎变得更加高效。我特别希望能够提供智能手机在渲染方面遇到的具体问题的实例。 - Robin Winslow
我在网上找不到任何关于移动浏览器因为低效选择器而渲染困难的例子,但是我确实找到了一些略微过时的例子,人们试图对各种低效的CSS选择器进行一些量化的数字分析。我已经相应地更新了我的答案,希望你会发现它有用。 - Simon West
太棒了,这正是我在寻找的资源类型。从这两篇文章中得出的主要结论似乎是,即使你真的试图编写低效的查询,它也只会产生微不足道的差异,这正是我想要的结论。如果我们能找到包括移动设备在内的任何测试,那将是太棒了。我会暂时保留这个问题,看看其他人能提出什么答案,但这绝对是最优秀的答案之一。 - Robin Winslow

4
对于如此丰厚的赏金,我愿意冒险回答“空”的答案:没有官方的CSS选择器会导致渲染速度明显变慢,而且在快速计算机和浏览器迭代的今天,任何发现的问题都会很快由浏览器制造商解决。即使在移动浏览器中,除非轻率的开发人员愿意使用非标准的jQuery选择器,否则不会有问题。这些被jQuery开发人员标记为风险,并且确实可能会有问题。
在这种情况下,缺乏证据就是缺乏问题的证据。因此,请使用语义化标记(特别是OOCSS),并在使用标准CSS选择器时在晦涩的浏览器中报告任何减速情况。
未来的人们:2012年的CSS性能问题已经成为过去式。

1

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

'不要使用@import'通过stevesouders.com

该文章包含许多速度测试示例,介绍了每种类型之间的差异,以及将一种类型与另一种类型结合使用的情况(例如:通过<link>调用的CSS文件还包含对另一个CSS文件的@import)。


离题了,而且这也是我想象中大多数前端开发人员已经知道的更简单的性能调整之一。 - Robin Winslow
1
也许是这样,但我们不应该假设。掌握基础知识从来都不是坏事。 - justacoder
除非它离题了 :p - Robin Winslow

1

CSS不是一种提高网站速度的无关紧要的方式,当你考虑性能时,它必须是你最后考虑的事情。以适合自己的方式编写CSS,编译它,然后将其放在头部。这可能有些粗糙,但是在查看浏览器性能时还有很多其他要注意的事项。如果你在数字机构工作,你不会因为额外的1毫秒加载时间而得到报酬。

正如我所评论的,使用Chrome的PageSpeed,这是一个谷歌工具,可以分析网站的27个参数,其中包括CSS。

我的帖子只涉及到一个问题,您是否宁愿让约99%的网络用户能够打开网站并正确地查看它,即使是使用IE7等浏览器的人也可以。而不是通过使用CSS3关闭大约10%的用户(如果结果是您可以获得额外的1-10毫秒性能)。

大多数人至少拥有1mbit / 512kbit或更高速度的互联网连接,如果您加载一个重型网站,需要大约3秒钟才能加载,但您可以节省10毫秒的CSS加载时间吗?

当涉及到移动设备时,您应该为移动设备制作专门的网站,这样当您拥有屏幕尺寸小于“宽度”px的设备时,您就有了一个单独的网站。

请在下面发表评论,这是我对Web开发的看法和个人经验。


这些性能实践是众所周知和被接受的。这个问题涉及渲染性能。考虑到渲染问题比传输问题不那么重要,我正在尝试找出渲染性能有多重要,以及选择器或渲染规则必须变得多么复杂才会有影响。感谢您在“根本不重要”的一方发表意见,但除此之外,这个答案实际上并没有为辩论做出贡献。 - Robin Winslow
它的贡献在于所有设备都以渲染过程太快的方式进行,除非您使用150dpi或更高分辨率的图片,否则真的没有必要深入研究,因为网络只显示72dpi。我还想补充一点,如果您可以在浏览器中呈现3D,则2D比关心速度要快得多。但是希望您能找到一些证明它显着更快的证据,我已将其添加为收藏夹。 - Simon Dragsbæk
好的,你提到的150dpi正是我正在寻找的东西 - 但我想要证据,而不仅仅是你的断言:证明150dpi确实有所改进,并且其他渲染方面也没有问题。我个人认为,一定有一些网站设计非常复杂,至少在移动设备上渲染CSS会有一点慢。 - Robin Winslow
我明白你的意思,但在网页上它仍然只有72dpi,但要呈现150dpi,你当然必须呈现两倍的像素。如果你将下载速度加入到渲染时间中,你可能会有一个案例,比如使用CSS 3或CSS 2制作圆角,那么你就需要考虑下载时间和渲染时间,而不是仅仅考虑渲染时间。 - Simon Dragsbæk

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接