解释如何优化css选择器_优化CSS:ID选择器和其他神话

解释如何优化css选择器

In today’s typical scenario, where the average website ships 500KB of gzipped JavaScript and 1.5MB of images, running on a midrange Android device via 3G with a 400ms round trip time, CSS selector performance is the least of our problems.

在当今的典型情况下,每个网站通常通过3G在中端Android设备上运行500KB压缩JavaScript和1.5MB图像,往返时间为400ms,CSS选择器的性能是我们遇到的最少问题。

Still, there’s something to be said about the topic, especially to weed out some of the myths and legends surrounding them. So let’s dive right in.

尽管如此,关于这个话题还有很多话要说,尤其是要清除掉围绕它们的一些神话传说。 因此,让我们直接进入。

CSS解析的基础 (The Basics of CSS Parsing)

First, to get on the same page — this article isn’t about the performance of CSS properties and values. What we’re covering here is the performance cost of the selectors themselves. I’ll be focusing on the Blink rendering engine, specifically Chrome 62.

首先,要进入同一页面-本文与CSS属性和值的性能无关。 我们在这里讨论的是选择器本身的性能成本。 我将重点介绍Blink渲染引擎,特别是Chrome 62。

The selectors can be split into a few groups and (roughly) sorted from the least to most expensive:

选择器可以分为几组,并且(从最便宜到最昂贵)大致分类:

ranktypeexample
1.ID#classID
2.Class.class
3.Tagdiv
4.General and adjacent siblingdiv ~ a, div + a
5.Child and descendantdiv > a, div a
6.Universal*
7.Attribute[type="text"]
8.Pseudo-classes and elementsa:first-of-type, a:hover
类型
1。 ID #classID
2。 .class
3。 标签 div
4。 普通兄弟姐妹 div ~ adiv + a
5, 子孙 div > adiv a
6。 普遍 *
7 属性 [type="text"]
8。 伪类和元素 a:first-of-typea:hover

Does this mean that you should only use IDs and classes? Well … not really. It depends. First, let’s cover how browsers interpret CSS selectors.

这是否意味着您应该只使用ID和类? 好吧……不是真的。 这取决于。 首先,让我们介绍浏览器如何解释CSS选择器。

Browsers read CSS from right to left. The rightmost selector in a compound selector is known as the key selector. So, for instance, in #id .class > ul a, the key selector is a. The browser first matches all key selectors. In this case, it finds all elements on the page that match the a selector. It then finds all ul elements on the page and filters the as down to just those elements that are descendants of uls — and so on until it reaches the leftmost selector.

浏览器从右到左阅读CSS。 复合选择器中最右边的选择器称为选择器。 因此,例如,在#id .class > ul a ,键选择器是a 。 浏览器首先匹配所有键选择器。 在这种情况下,它将查找页面上与a选择器匹配的所有元素。 然后,它会找到页面上的所有ul元素,并将a过滤为ul s的后代元素,依此类推,直到到达最左边的选择器为止。

Therefore, the shorter the selector, the better. If possible, make sure that the key selector is a class or an ID to keep it fast and specific.

因此,选择器越短越好。 如果可能,请确保键选择器是类或ID,以使其保持快速且特定的状态。

衡量绩效 (Measuring the Performance)

Ben Frain created a series of tests to measure selector performance back in 2014. The test consisted of an enormous DOM comprising 1000 identical elements, and measuring the speed it took to parse various selectors, ranging from IDs to some seriously complicated and long compound selectors. What he found was that the delta between the slowest and fastest selector was ~15ms.

Ben Frain在2014年创建了一系列测试来衡量选择器的性能。该测试包括一个庞大的DOM,其中包含1000个相同的元素,并测量解析各种选择器所需的速度,从ID到一些严重复杂且冗长的复合选择器。 他发现最慢的选择器和最快的选择器之间的间隔约为15ms。

However, that was back in 2014. Things have changed a lot since then, and memorizing rules is all but useless in the ever-changing browser landscape. Always remember to do your own tests, especially when performance is concerned.

但是,那是在2014年。事情从那时起发生了很大变化,在不断变化的浏览器环境中,记住规则几乎毫无用处。 永远记得做自己的测试,尤其是在性能方面。

I went to do my own tests, and for that I used Paul Lewis’ test mentioned in Paul Irish’s comment expressing concern over the useful, yet convoluted “quantity selectors”:

我去做自己的测试,为此,我使用了保罗·爱尔兰的评论中提到的保罗 ·刘易斯的测试,对有用但复杂的“ 数量选择器 ”表示关注:

These selectors are among the slowest possible. ~500 slower than something wild like “div.box:not(:empty):last-of-type .title”. Test page http://jsbin.com/gozula/1/quiet

这些选择器是最慢的选择器。 比诸如“ div.box:not(:empty):last-of-type .title”之类的狂放东西慢约500。 测试页http://jsbin.com/gozula/1/quiet

The test was bumped up a bit, to 50000 elements, and you can test it out yourself. I did an average of 10 runs on my 2014 MacBook Pro, and what I got was the following:

该测试稍微增加到了50000个元素,您可以自己进行测试 。 我在2014 MacBook Pro上平均运行了10次,得到的结果如下:

SelectorQuery Time (ms)
div4.8740
.box3.625
.box > .title4.4587
.box .title4.5161
.box ~ .box4.7082
.box + .box4.6611
.box:last-of-type3.944
.box:nth-of-type(2n - 1)16.8491
.box:not(:last-of-type)5.8947
.box:not(:empty):last-of-type .title8.0202
.box:nth-last-child(n+6) ~ div20.8710
选择器 查询时间(毫秒)
div 4.8740
.box 3.625
.box > .title 4.4587
.box .title 4.5161
.box ~ .box 4.7082
.box + .box 4.6611
.box:last-of-type 3.944
.box:nth-of-type(2n - 1) 16.8491
.box:not(:last-of-type) 5.8947
.box:not(:empty):last-of-type .title 8.0202
.box:nth-last-child(n+6) ~ div 20.8710

The results will of course vary depending on whether you use querySelector or querySelectorAll, and the number of matching nodes on the page, but querySelectorAll comes closer to the real use case of CSS, which is targeting all matching elements.

当然,结果会有所不同,具体取决于您使用的是querySelector还是querySelectorAll ,以及页面上匹配节点的数量,但是querySelectorAll更接近于CSS的实际用例,它以所有匹配元素为目标。

Even in such an extreme case, with 50000 elements to match, and using some really insane selectors like the last one, we find that the slowest one is ~20ms, while the fastest is the simple class at ~3.5ms. Not really that much of a difference. In a realistic, more “tame” DOM, with around 1000–5000 nodes, you can expect those results to drop by a factor of 10, bringing them to sub-millisecond parsing speeds.

即使在这样的极端情况下,也可以匹配50000个元素,并使用一些真正疯狂的选择器(例如最后一个选择器),我们发现最慢的选择器约为20ms,而最快的选择器约为3.5ms。 没有太大的区别。 在具有大约1000-5000个节点的现实的,更“驯服”的DOM中,您可以预期这些结果将下降10倍,使它们的解析速度达到亚毫秒级。

What we can see from this test is that it’s not really worth it to worry over CSS selector performance. Just don’t overdo it with pseudo selectors and really long selectors. We can also see how Blink improved in the last two years. Instead of the stated ~500x slowdown for a “quantity selector” (.box:nth-last-child(n+6) ~ div) compared to an “insanity selector” (.box:not(:empty):last-of-type .title), we only see a ~1.5x slowdown. That’s an amazing improvement, and we can only expect browsers to get better, making CSS selector performance even less impactful.

从该测试中我们可以看到,担心CSS选择器的性能确实不值得。 只是不要用伪选择器和很长的选择器来过度使用它。 我们还可以看到Blink在过去两年中如何改进。 与“疯狂选择器”( .box:not(:empty):last-of-type .title )相比,“数量选择器”( .box:nth-last-child(n+6) ~ div表示的速度降低了约500倍.box:not(:empty):last-of-type .title ),我们只会看到约1.5倍的减速。 这是一个了不起的改进,我们只能期望浏览器会变得更好,从而使CSS选择器性能的影响降低。

You should, however, stick to using classes whenever possible, and adopt some sort of namespacing convention like BEM, SMACSS or OOCSS, since it will not only help your website’s performance but vastly help with code maintainability. Overqualified compound selectors, especially when used with tag and universal selectors — such as .header nav ul > li a > .inner — are extremely brittle and a source of many unforeseen errors. They are also a nightmare to maintain, especially if you inherit the code from someone else.

但是,您应该尽可能地使用类,并采用某种命名间隔惯例,例如BEM,SMACSS或OOCSS,因为这不仅会提高网站的性能,而且在很大程度上有助于代码的可维护性。 合格的复合选择器,特别是与标记和通用选择器结合使用时,例如.header nav ul > li a > .inner ,它们非常脆弱,并且会导致许多无法预料的错误。 它们也是维护的噩梦,特别是如果您从其他人那里继承代码。

质量而不是数量 (Quality over Quantity)

A bigger problem of simply having expensive selectors is having a lot of them. This is know as “style bloat”, and you’ve probably seen the problem a lot. Typical examples are sites which import entire CSS frameworks like Bootstrap or Foundation, while using less than 10% of the transferred CSS. Another example is seen in old, never-refactored projects whose CSS has devolved into, as I like to call them, “Chronological Style Sheets” — CSS with a ton of appended classes to the end of the file as the project has changed and grown, now looking more like an overgrown garden full of weeds.

仅仅拥有昂贵的选择的一个更大的问题是有很多他们。 这被称为“样式膨胀”,您可能已经看到很多问题了。 典型示例是导入整个CSS框架(如Bootstrap或Foundation)的网站,而这些网站使用的转移CSS不到10%。 另一个例子出现在旧的,从未重构的项目中,这些项目CSS已经转移到了我所说的“年代样式表”中-随着项目的变化和发展,在文件末尾添加了很多附加类CSS ,现在看起来更像是杂草丛生的杂草丛生的花园。

Not only does a large CSS file take longer to transfer, (and network is the biggest bottleneck in website performance), they also take longer to parse. As well as constructing the DOM from your HTML, the browser needs to construct a CSSOM (CSS Object Model) to compare it with the DOM and match the selectors.

大型CSS文件不仅传输时间更长(而且网络是网站性能的最大瓶颈),解析时间也更长。 除了从HTML构造DOM之外,浏览器还需要构造CSSOM(CSS对象模型)以将其与DOM进行比较并匹配选择器。

So, keep your styles lean and DRY, don’t include everything and the kitchen sink, load what you need and when you need it, and use UNCSS if you need to.

因此,保持样式简洁干爽,不要包含所有内容和厨房水槽,不要在需要时加载所需的东西,并在需要时使用UNCSS

If you want to dig more into how the browsers parse CSS, check out Nicole Sullivan’s post on Webkit, Ilya Grigorik’s article on how Blink does it, or Lin Clark’s article on Mozilla’s new Stylo CSS engine.

如果您想进一步了解浏览器如何解析CSS, 请查看Nicole Sullivan在Webkit上的帖子Ilya Grigorik的有关Blink如何实现的文章 ,或Lin Clark的有关Mozilla的新Stylo CSS引擎的文章

房间里的大象:风格失效 (The Elephant in the Room: Style Invalidation)

What we’ve covered so far is fine, but we’ve only discussed a single rendering pass. Today’s websites are no longer static documents, but resemble apps with dynamic content users can interact with.

到目前为止,我们所介绍的内容还不错,但是我们仅讨论了一个渲染过程。 如今的网站不再是静态文档,而是类似于具有动态内容的应用程序,用户可以与之交互。

This complicates things, since parsing CSS is only a single step in the browser rendering pipeline. Here’s a render-oriented view of how a browser renders a single frame to the screen (source: Google):

这使事情变得复杂,因为解析CSS只是浏览器呈现管道中的一步。 这是浏览器如何将单个框架呈现到屏幕的面向渲染的视图(来源: Google ):

The browser rendering pipeline

We won’t be going into JavaScript performance and compositing, but will focus instead on the purple part — style parsing and laying out the elements.

我们不会讨论JavaScript性能和合成,而是将重点放在紫色部分-样式解析和布局元素。

After constructing the DOM and CSSOM, the browser needs to combine the two into a render tree before finally painting it on the screen. In that step, the browser needs to figure out the computed CSS for each matching element. You can see this yourself in the Elements > Styles panel of the developer tools. It takes all the matching styles, the cascade, and browser-specific user agent styles to construct the final, computed CSS for the element.

构造完DOM和CSSOM之后,浏览器需要将两者合并成一个渲染树,然后才能最终将其绘制在屏幕上。 在这一步中,浏览器需要为每个匹配元素找出计算出CSS。 您可以在开发人员工具的“ 元素”>“样式”面板中自己查看。 它采用所有匹配的样式,级联和特定于浏览器的用户代理样式来构造该元素的最终计算出CSS。

It can then proceed to the layout (also known as reflow) step, where it computes the geometry, and constructs the box model of the page, placing each element on its respective position on the viewport. Layout is the most computationally intensive part of this process.

然后,它可以继续进行布局(也称为重排)步骤,在此步骤中,它计算几何形状,并构造页面的框模型,将每个元素放置在视口中其各自的位置上。 布局是此过程中计算量最大的部分。

Finally, the browser converts each node in the render tree to actual pixels on the screen in the paint stage.

最后,浏览器在绘制阶段将渲染树中的每个节点转换为屏幕上的实际像素。

Now, what happens when we mutate the DOM by changing some classes on the page, adding or removing some nodes, modifying some attributes, or in any way messing with the HTML (or the stylesheets themselves)?

现在,当我们通过更改页面上的某些类,添加或删除某些节点,修改某些属性或以任何方式弄乱HTML( 或样式表本身 )来对DOM进行更改时会发生什么?

We invalidate the computed styles and the browser needs to invalidate everything down the tree of the matched selectors. While today’s browsers are much smarter, it used to be the case that if you changed a class on the body element, all the descendant elements needed to have their computed styles recalculated.

我们使计算出的样式无效,浏览器需要使匹配选择器树下的所有内容无效。 尽管当今的浏览器更加智能,但过去经常发生的情况是,如果您更改body元素上的类,则需要重新计算所有后代元素,以重新计算其计算的样式。

One way to avoid this issue is to reduce the complexity of your selectors. Instead of writing #nav > .list > li > a, use a single selector, like .nav-link. That way, you reduce the scope of style invalidation, since if you modify anything inside the #nav, you won’t trigger recalculations for the entire node.

避免此问题的一种方法是降低选择器的复杂性。 不用编写#nav > .list > li > a ,而是使用单个选择器,如.nav-link 。 这样,您可以减少样式无效的范围,因为如果您在#nav内进行任何修改,则不会触发整个节点的重新计算。

Another way is to reduce the scope — such as the number of invalidated elements. Be specific with your CSS. Keep this in mind especially during animations, where the browser has only ~10ms to do all the work required.

另一种方法是减小范围,例如无效元素的数量。 具体说明您CSS。 请记住这一点,尤其是在动画中,在该动画中浏览器仅需要10毫秒即可完成所有所需的工作。

If you want to get down to the nitty gritty details of style invalidation, I recommend reading Style Invalidation in Blink.

如果您想深入了解样式无效的细节,建议阅读Blink中的样式无效

结论 (Conclusion)

To sum it up, you shouldn’t worry about selector performance, unless you really go overboard. While the topic was all the rage in 2012, browsers have gotten a lot faster and smarter since. Even Google doesn’t worry about it anymore. If you check out Google’s Page Speed Insights page, you won’t see the rule “Use efficient CSS selectors” which was removed in 2013.

综上所述,除非您真的选择过多,否则您不必担心选择器的性能。 虽然主题是所有的愤怒在2012年,浏览器已经获得了很多更快,更聪明以来。 甚至Google也不再担心。 如果您查看Google的Page Speed Insights页面 ,则不会看到规则“使用有效CSS选择器” 已在2013年删除。

Instead, focus on making your CSS maintainable and readable. Your colleagues and your future self will thank you for it. Try to optimize the CSS delivery by including only the used styles. And after that, get to know the rendering pipeline. Unlike selectors, styles themselves can be expensive, and the difference between a janky site and a smooth one can often be found in how the CSS is implemented.

相反,请专注于使CSS可维护和可读。 您的同事和您未来的自我将为此感谢您。 通过仅包括使用的样式来尝试优化CSS交付。 然后,了解渲染管道。 与选择器不同,样式本身可能会很昂贵,而一个混乱站点和一个平滑站点之间的区别通常可以在CSS的实现方式中找到。

And as a final note: always do your own tests.

最后一点:始终进行自己的测试。

Don’t just believe what someone wrote on the internet a few years ago. The landscape is changing drastically and at an incredible pace. What’s relevant today might become obsolete sooner than you know.

不要仅仅相信几年前某人在互联网上写的东西。 景观正在以惊人的速度急剧变化。 今天重要的事情可能早于您所知道的就过时了。

翻译自: https://www.sitepoint.com/optimizing-css-id-selectors-and-other-myths/

解释如何优化css选择器

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值