编写高效的 CSS

本文档提供了优化 CSS 代码的指南,特别是如何书写高效选择器。

CSS 规范没有规定浏览器如何去实现样式系统,仅仅是说明了它们该实现哪些内容。有鉴于此,不同的样式系统引擎可能会拥有完全不同的性能和行为,特别是 Gecko 与 WebKit, 这两个引擎都是开源项目,实现了类似的算法,具有同样的优缺点。因此下面介绍的小技巧对于真实世界的 Web 文档会十分有效。

第一部分内容综合讨论了常见的样式系统是如何分类规则的。接下来的部分包含了书写规则的指南,它利用了前面讨论的系统的优点。

样式系统如何拆分规则

样式系统将规则拆分成四个主要类别:

  1. ID 规则
  2. Class 规则
  3. 标签规则
  4. 通用规则

理解这些分类是关键,因为它们是构建规则匹配块的基础。

我在下面的段落中使用了术语 关键选择器(key selector) 。选择器的最后部分即为关键选择器(即用来匹配目标元素的那部分,而不是该元素的父辈元素)。

例如,在下面规则中…

a img, div > p, h1 + [title] {…}

…关键选择器为 img, p,与 title.

ID 规则

这一类别包含了那些将 ID 选择器作为关键选择器的规则。

示例
button#backButton {…} /* This is an ID-categorized rule */
#urlBar[type="autocomplete"] {…} /* This is an ID-categorized rule */
treeitem > treerow > treecell#myCell:active {…} /* This is an ID-categorized rule */

Class 规则

如果一个规则将一个 class 作为它的关键选择器,那么它就属于该类别。

示例
button.toolbarButton {…} /* A class-based rule */
.fancyText {…}	/* A class-based rule */
menuitem > .menu-left[checked="true"] {…} /* A class-based rule */

标签规则

如果既没有 class 也没有 ID 来作为关键选择器,那么接下来的候选者就是 标签 类别。 如果一条规则将一个标签作为它的关键选择器,那么这条规则就属于该类别。

示例
td {…} /* A tag-based rule */
treeitem > treerow {…} /* A tag-based rule */
input[type="checkbox"] {…} /* A tag-based rule */

通用规则

不属于上面那些类别的规则都属于这个类别。

示例
[hidden="true"] {…} /* A universal rule */  
* {…}		/* A universal rule */
tree > [collapsed="true"] {…} /* A universal rule */

样式系统如何匹配规则

样式系统从关键选择器开始匹配规则,然后左移(查找规则选择器的任何祖先元素)。只要选择器的子树(substree)一直在检查,样式系统就会持续左移,直到和规则匹配,或者是因为不匹配而放弃。

规则过滤是你需要学习的最基础的概念。分类存在的意义就是过滤掉无关的规则(这样样式系统就不会浪费时间去匹配它们了)。

这就是能够极大增加性能的关键。对于一个给定的元素,需要匹配的规则越少,样式的解析就会越快。

举个例子,如果一个元素拥有一个 ID,那么只有匹配该 ID 的 ID 规则才会被检查。同理,只有当 Class 规则中的 class 出现在元素上时该规则才被检查。只有当标签规则的标签匹配时该规则才被检查。通用规则始终都会检查。

高效 CSS 指南

避免通用规则

规则不能以通用类型作为结束!

不要用标签或 class 来限制 ID 规则

如果规则的关键选择器为 ID 选择器,则没有必要为规则增加标签名。因为 ID 是唯一的,增加标签只会拖慢匹配过程。

button#backButton {…}
.menu-left#newMenuIcon {…}
#backButton {…}
#newMenuIcon {…}
例外:When it’s desirable to change the class of an element dynamically in order to apply different styles in different situations, but the same class is going to be shared with other elements.

不要用标签名限制 class 规则

前面那节内容在这里同样适用。虽然在同一页面能够多次使用 class,但它仍然比标签名更独特。

按照惯例,你可以将标签名写到 class 名里。当然,这会有损灵活性;如果设计更改,标签变动,class 名也要跟着变动。(最好的办法是选择严格语义化的名字,毕竟分离样式表的一个目标就是为了灵活性。)

treecell.indented {…}
.treecell-indented {…}
.hierarchy-deep {…}

尽量使用最具体的类别

解析速度变慢的最大原因就是绝大多数规则都落进了标签类别中。为元素增加 class,我们就可以进一步的将这些规则划分到 Class 类别中,这将减少用于匹配标签的时间。

treeitem[mailfolder="true"] > treerow > treecell {…}
.treecell-mailfolder {…}

避免后代选择器

后代选择器是 CSS 中耗费最昂贵的选择器。 它的耗费是极其昂贵的—特别是当选择器在标签或通用类别中。

通常我们在意的是 子选择器。比如说,当性能十分差的时候,Firefox 的 UI  CSS 将不需要任何理由的禁止掉子选择器。你也应该在网页中这么做。

treehead treerow treecell {…}
略好,但还是差(查看下一条指南)
treehead > treerow > treecell {…}

属于标签类别的规则永远不要包含子选择器

标签类别的规则中避免使用子选择器。否则的话,在该元素出现的所有地方,匹配时间都将极大延长(特别是当规则很可能会被匹配) 。

treehead > treerow > treecell {…}
.treecell-header {…}

在使用子选择器的地方想想为什么

当使用子选择器时要十分谨慎。能免则免。

一般来说,子选择器常常用于 RDF 树与菜单:

treeitem[IsImapServer="true"] > treerow > .tree-folderpane-icon {…}

要记住,模板中的 REF 特性可以重复出现!好好利用这一优点。在子 XUL 元素上重复使用 RDF 属性,这样可以基于该属性来修改元素。

GOOD
.tree-folderpane-icon[IsImapServer="true"] {…}

依赖继承

了解哪些属性能够继承,然后允许它们这样做!

For example, XUL widgets are explicitly set up such that a parent’s list-style-image or font rules will filter down to anonymous content. It’s not necessary to waste time on rules that talk directly to anonymous content.

BAD
#bookmarkMenuItem > .menu-left { list-style-image: url(blah) }
GOOD
#bookmarkMenuItem { list-style-image: url(blah) }

In the above example, the desire to style anonymous content (without leveraging the inheritance of list-style-image) resulted in a rule that was in the Class Category, when the rule should have ended up in the ID Category—the most specific category of all!

Remember: Elements all have the same classes—especially anonymous content!

The above “bad” rule forces every menu’s icons to be tested for containment within the bookmarks menu item. Since there are many menus, this is extraordinarily expensive.  Instead, the “good” rule limits the testing to the bookmarks menu.

Use -moz-image-region!

Putting a bunch of images into a single image file and selecting them with -moz-image-region performs significantly better than putting each image into its own file.

Use scoped stylesheets

If you specify a stylesheet as an XBL resource, the styles only apply to the bound elements and their anonymous content. This reduces the inefficiency of universal rules and child selectors because there are fewer elements to consider.

Original Document Information

Document Tags and Contributors

Contributors to this page: sunnylost
最后编辑者: sunnylost,