博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
解释如何优化css选择器_优化CSS:ID选择器和其他神话
阅读量:2507 次
发布时间:2019-05-11

本文共 13169 字,大约阅读时间需要 43 分钟。

解释如何优化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:

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

rank type example
1. ID #classID
2. Class .class
3. Tag div
4. General and adjacent sibling div ~ a, div + a
5. Child and descendant div > a, div a
6. Universal *
7. Attribute [type="text"]
8. Pseudo-classes and elements a: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 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 expressing concern over the useful, yet convoluted “”:

我去做自己的测试,为此,我使用了保罗提到的 ·刘易斯的测试,对有用但复杂的“ ”表示关注:

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 . I did an average of 10 runs on my 2014 MacBook Pro, and what I got was the following:

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

Selector Query Time (ms)
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
选择器 查询时间(毫秒)
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 if you need to.

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

If you want to dig more into how the browsers parse CSS, , , or .

如果您想进一步了解浏览器如何解析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: ):

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

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 ()?

现在,当我们通过更改页面上的某些类,添加或删除某些节点,修改某些属性或以任何方式弄乱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 .

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

结论 (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 , you won’t see the rule “Use efficient CSS selectors”

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

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 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.

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

翻译自:

解释如何优化css选择器

转载地址:http://ixegb.baihongyu.com/

你可能感兴趣的文章
用 async/await 来处理异步
查看>>
app开发-1
查看>>
在JavaScript中调用ASP.NET WebService的简单方法
查看>>
jQuery基础知识,很赞的!!!
查看>>
[SDOI 2012]Longge的问题
查看>>
简单BBS项目开始(一)
查看>>
[Codeforces 925C]Big Secret
查看>>
处理MVC中默认的Json方法返回时间的问题
查看>>
分布式技术追踪 2018年第十期
查看>>
IDEA中Git的使用
查看>>
War3模型导出
查看>>
java: 列出本机java环境
查看>>
Python内置函数(19)——eval
查看>>
怎样录制屏幕并将结果保存为Gif
查看>>
别名设置 alias
查看>>
练习3.34
查看>>
oracle加减操作
查看>>
dp乱写3:环形区间dp(数字游戏)
查看>>
【Beta阶段】启程会议——第零次Scrum Meeting!
查看>>
Apple Tree
查看>>