根据计算复杂度选择有效的选择器
Posted
技术标签:
【中文标题】根据计算复杂度选择有效的选择器【英文标题】:Choosing efficient selectors based on computational complexity 【发布时间】:2012-06-28 10:36:08 【问题描述】:鉴于 CSS 处理细节,尤其是 RTL matching 和 selector efficiency,纯粹从渲染引擎性能的角度应该如何编写选择器?
这应该涵盖一般方面,包括使用或避免使用伪类、伪元素和关系选择器。
【问题讨论】:
好问题,但永远记住:性能不是一切,直到它成为唯一需要担心的事情。仅当您确定绝对需要尽可能多地保存并且您的设计允许时才开始担心性能。 Chrome 开发者工具中的“CSS 选择器分析器”非常有用,如果你想...分析你的 CSS 选择器。 【参考方案1】:在运行时,html 文档被解析成一个 DOM 树,其中包含平均深度为 D
的 N
元素。在所应用的样式表中还一共有S
CSS 规则。
元素的样式是单独应用的,这意味着 N
和整体复杂性之间存在直接关系。值得注意的是,这可能会被浏览器逻辑所抵消,例如引用缓存和从相同元素回收样式。例如,以下列表项将应用相同的 CSS 属性(假设未应用 :nth-child
等伪类):
<ul class="sample">
<li>one</li>
<li>two</li>
<li>three</li>
</ul>
选择器从右到左匹配单个规则资格 - 即,如果最右边的键与特定元素不匹配,则无需进一步处理选择器并将其丢弃。这意味着最右边的键应该匹配尽可能少的元素。下面,p
描述符将匹配更多元素,包括目标容器的 outside 段落(当然,不会应用该规则,但仍会导致对该特定选择器的资格检查进行更多迭代):
.custom-container p
.container .custom-paragraph
关系选择器:后代选择器最多需要迭代 D
元素。例如,如果元素处于父子关系中,成功匹配.container .content
可能只需要一步,但在确认元素不匹配之前,需要遍历 DOM 树一直到html
规则安全地丢弃。这也适用于链式后代选择器,但有一些限制。
另一方面,>
子选择器、+
相邻选择器或 :first-child
仍需要评估其他元素,但隐含深度仅为 1,并且永远不需要进一步的树遍历。
:before
和 :after
等伪元素的 behavior definition 暗示它们不是 RTL 范式的一部分。假设的逻辑是,在规则指示将其插入元素内容之前或之后之前,本身没有伪元素(这反过来需要额外的 DOM 操作,但不需要额外的计算来匹配选择器本身)。
我找不到任何关于伪类的信息,例如:nth-child()
或:disabled
。验证元素状态需要额外的计算,但从规则解析的角度来看,只有将它们排除在 RTL 处理之外才有意义。
鉴于这些关系,O(N*D*S)
的计算复杂性应该主要通过最小化 CSS 选择器的深度和解决上述第 2 点来降低。与单独减少 CSS 规则或 HTML 元素的数量相比,这将带来可量化的更强改进^
浅的,最好是一层,特定的选择器处理得更快。这被谷歌带到了一个全新的水平(以编程方式,而不是手动!),例如很少有三键选择器和搜索结果中的大部分规则看起来像
#gb
#gbz, #gbg
#gbz
#gbg
#gbs
.gbto #gbs
#gbx3, #gbx4
#gbx3
#gbx4
/*...*/
^ - 虽然从渲染引擎性能的角度来看确实如此,但总会有额外的因素,例如流量开销和 DOM 解析等。
来源:12345
【讨论】:
在选择器中,所有的伪元素(不仅仅是::before
和::after
)都遵循相同的规则,它们只能应用于选择器的主题,并且只在选择器之后进行评估匹配完成 - w3.org/TR/selectors/#pseudo-elements 从 CSS1 到 CSS3,这始终是关键选择器;但是在 CSS4 中这可能会改变。
RTL 解析是一个实现细节,它可能因引擎而异,但从键选择器开始并向后工作的一般概念已在供应商之间达成一致。首先评估每个复合选择器中的哪些简单选择器似乎只有源代码才能回答......更多关于here。
@BoltClock:好点;我愿意推测 CSS4 父选择器实际上将是一个具有 has-children 条件的伪类 - 否则可能会有很多冗余匹配循环。至于特定于代码的实现,他们可能最好坚持建议的行为 - 一个显着的搞砸例子是 IE7 缓存 :first-child
引用以上是关于根据计算复杂度选择有效的选择器的主要内容,如果未能解决你的问题,请参考以下文章