基于计算复杂度选择高效选择器

6

考虑到CSS处理的细节,尤其是RTL匹配选择器效率,从渲染引擎性能的角度纯粹地编写选择器应该如何进行?

这应该涵盖一般方面,包括使用或避免伪类、伪元素和关系选择器。


1
好问题,但请记住:性能并不是一切,除非它是你唯一需要担心的事情。只有在确信需要尽可能节省资源且设计允许的情况下才开始关注性能。 - BoltClock
2
Chrome开发者工具中的“CSS选择器分析器”非常有用,如果您想要分析您的CSS选择器。 - thirtydot
1个回答

9

运行时,一个HTML文档会被解析成一个DOM树,其中包含平均深度为DN个元素。同时,应用的样式表中共有S个CSS规则。

  1. Elements' styles are applied individually meaning there is a direct relationship between N and overall complexity. Worth noting, this can be somewhat offset by browser logic such as reference caching and recycling styles from identical elements. For instance, the following list items will have the same CSS properties applied (assuming no pseudo-classes such as :nth-child are applied):

    <ul class="sample">
      <li>one</li>
      <li>two</li>
      <li>three</li>
    </ul>
    
  2. Selectors are matched right-to-left for individual rule eligibility - i.e. if the right-most key does not match a particular element, there is no need to further process the selector and it is discarded. This means that the right-most key should match as few elements as possible. Below, the p descriptor will match more elements including paragraphs outside of target container (which, of course, will not have the rule apply but will still result in more iterations of eligibility checking for that particular selector):

    .custom-container p {}
    .container .custom-paragraph {}
    
  3. Relationship selectors: descendant selector requires for up to D elements to be iterated over. For instance, successfully matching .container .content may only require one step should the elements be in a parent-child relationship, but the DOM tree will need to be traversed all the way up to html before an element can be confirmed a mismatch and the rule safely discarded. This applies to chained descendant selectors as well, with some allowances.

    On the other hand, a > child selector, an + adjacent selector or :first-child still require an additional element to be evaluated but only have an implied depth of one and will never require further tree traversal.

  4. The behavior definition of pseudo-elements such as :before and :after implies they are not part of the RTL paradigm. The logic the assumption is that there is no pseudo element per se until a rule instructs for it to be inserted before or after an element's content (which in turn requires extra DOM manipulation but there is no additional computation required to match the selector itself).

  5. I couldn't find any information on pseudo-classes such as :nth-child() or :disabled. Verifying an element state would require additional computation, but from the rule parsing perspective it would only make sense for them to be excluded from RTL processing.

在这些关系中,计算复杂度 O(N*D*S) 应该主要通过最小化 CSS 选择器的深度和解决上述第二点来降低。与仅最小化 CSS 规则或 HTML 元素数量相比,这将产生可量化的更强的改进。
浅显易懂,最好只有一个级别的具体选择器处理速度更快。例如 Google(通过编程,而非手动!)将其推向了一个全新的水平,几乎没有使用三键选择器,并且大多数搜索结果中的规则看起来都像。
#gb {}
#gbz, #gbg {}
#gbz {}
#gbg {}
#gbs {}
.gbto #gbs {}
#gbx3, #gbx4 {}
#gbx3 {}
#gbx4 {}
/*...*/

^ - 虽然从渲染引擎性能的角度来看这是正确的,但总会存在其他因素,如流量开销和DOM解析等。

来源: 1 2 3 4 5


1
在选择器中,所有伪元素(不仅仅是 ::before::after)都受到相同的规则约束,它们只能应用于选择器的主体,并且只有在选择器匹配完成后才会被评估 - http://www.w3.org/TR/selectors/#pseudo-elements 从 CSS1 到 CSS3,这始终是关键选择器; 然而在 CSS4 中可能会发生改变。 - BoltClock
1
RTL解析是一种实现细节,可能因引擎而异,但在厂商之间达成的共识是从关键选择器开始向后工作的一般概念。每个复合选择器中哪些简单选择器首先被评估似乎只有源代码才能回答...更多信息请参见此处:https://dev59.com/jWLVa4cB1Zd3GeqPxpEk#10108700。 - BoltClock
@BoltClock: 说得好; 我愿意猜测CSS4的父选择器将有效地成为一个带有has-children条件的伪类 - 否则可能会有很多冗余的匹配循环。至于代码特定的实现,最好还是坚持建议的行为 - 一个明显的搞砸例子是IE7对:first-child引用的缓存。 - Oleg

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