(转载)书写高效CSS

css 规范并不会强行规定浏览器必须如何实现样式系统,只会规定浏览器必须实现什么。因此,不同的样式系统引擎拥有不同的性能,开源的 Gecko 和 Webkit 算法类似,因此优缺点也类似。因此,下面的建议在大部分的 web 浏览器上应该是有意义的。

第一部分简单介绍样式系统规则分类;后面的章节主要指导如何利用样式系统的实现原理,来书写更有效率的规则。

样式系统如何建立规则

样式系统的规则分为四大类

  1. ID
  2. class
  3. tag
  4. 通用规则

理解这四类规则非常关键,这是规则匹配的基础。

在接下来的段落中,我会使用”关键选择“(key selector)术语。key selector 是选择器的最后一部分。

例如,在下面的规则中:

1
2
3
4
5
a img,
div > p,
h1 + title {
/*...*/
}

key selectorimg,ptitle

ID rules

第一类是使用 ID 选择器作为 key selector.

1
2
3
4
5
6
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 rules

第二类是使用了 class 选择器作为 key selector

1
2
3
4
5
6
button.toolbarButton {
} /* A class-based rule */
.fancyText {
} /* A class-based rule */
menuitem > .menu-left[checked='true'] {
} /* A class-based rule */

Tag rules

如果既不是 class 也不是 ID 作为 key selector,下一个候选是 tag

1
2
3
4
5
6
td {
} /* A tag-based rule */
treeitem > treerow {
} /* A tag-based rule */
input[type='checkbox'] {
} /* A tag-based rule */

Universal rules

剩下的均为此类。

1
2
3
4
5
6
[hidden='true'] {
} /* A universal rule */
* {
} /* A universal rule */
tree > [collapsed='true'] {
} /* A universal rule */

样式系统如何匹配规则

样式系统从 key selector 开始匹配,然后向左继续,寻找选择器的任意祖先。只要选择器的子树继续导出(这样翻译感觉怪怪的),样式系统会继续往左移动,直到要么匹配规则,要么因为不匹配终止。

最基本的概念是了解规则过滤。这些类别存在的目的是过滤掉不相关的规则,这样样式系统不用浪费时间去匹配它们。

这是提升性能的关键:检查一个指定的元素,规则越少,系统解析就越快。

例如,如果元素指定了 ID,那么只有与元素 ID 相同的 ID rule 会进行检查。只有存在于元素 class 列表的 class 规则才会进行检查。只有与标签相同的 tag rule 会被进行检查。通用规则总是会被检查。

高效 css 建议

避免使用通用规则

避免一个规则的结束是通用规则这一类。

不要使用 tag 或者 class 修饰 ID rule

如果一个规则使用了 ID 选择器作为 key selector,不要再添加 tag 标签指定。因为 ID 是唯一的,增加 tag name 会增加不必要的额外的匹配工作。

1
2
3
4
5
/* BAD */
button#backButton {
}
.menu-left#newMenuIcon {
}
1
2
3
4
5
/* GOOD */
#backButton {
}
#newMenuIcon {
}

例外:当需要通过改变元素的 class 来应用不同的样式,但是同样的 class 将会被其他元素共享。

不要使用 tag 修饰 class 规则

尽管 class 可以在同一个页面重复出现,但是它的唯一性比 tag 更强。

你可以约定在 class 名称中包含 tag 名称,但是这会损失一些灵活性。当设计改变 tag 时,class 也会跟着变化。最好的是使用语义名称的 class name

1
2
3
/* BAD */
treecell.indented {
}
1
2
3
/* GOOD */
.treecell-indented {
}
1
2
3
/* BEST */
.hierarchy-deep {
}

使用最特定的一类规则

tag 这类会匹配太多的规则,这会大大的降低效率。通过给元素增加 class,我们可以回 class 分类进行细分,减少匹配时间。

1
2
3
/* BAD */
treeitem[mailfolder='true'] > treerow > treecell {
}
1
2
3
/* GOOD */
.treecell-mailfolder {
}

避免使用后代选择器

后代选择器是 css 中最耗时的选择器。尤其当选择器是 tag 或者通用这一类。

1
2
3
/* BAD */
treehead treerow treecell {
}
1
2
3
/* BETTER, BUT STILL BAD (see next guideline) */
treehead > treerow > treecell {
}

tag 分类规则永远不要包含孩子选择器

避免使用孩子选择器在 tag 分类规则中。这会显著增长匹配时间。

1
2
3
/* BAD */
treehead > treerow > treecell {
}
1
2
3
/* GOOD */
.treecell-header {
}

质疑所有使用孩子选择器的地方

使用孩子选择器时务必谨慎,尽可能避免使用。

特别是,子选择器频繁适用于 RDF 树(这个自行 Google 吧)和 menus,如下:

1
2
3
/* BAD */
treeitem[IsImapServer='true'] > treerow > .tree-folderpane-icon {
}

依靠继承

了解哪些属性继承,然后使用继承。

1
2
3
4
/* BAD */
#bookmarkMenuItem > .menu-left {
list-style-image: url(blah);
}
1
2
3
4
/* GOOD */
#bookmarkMenuItem {
list-style-image: url(blah);
}
---- 本文结束,感谢您的阅读 ----