CSS的优先级

CSS 选择器的特异性(specificity)会影响 CSS 的匹配,特异性越高的规则,优先级越高,最终优先级最高的规则将命中,并覆盖优先级更低的属性。

如何计算特异性

https://specifishity.com/ 这个网站用一张图清晰地阐述了CSS选择器特异性的计算,感觉看完这个就不用往下看了😅。

首先,我们按以下几类情况分别计算:

  • A:ID 的个数
  • B:class、attributes、pseudo-class 的个数
  • C:type、pseudo-element 的个数

计算完成之后,他们的特异性可以写成 (A,B,C)

值得一提的是,有些 pseudo-class 会为其他选择器创建一个执行上下文,所以他们的特异性的计算方式会有一些不同:

  • :is():not():has()

    这三个伪类的特异性会替换成他们的参数中特异性最高的那个。如 :is(em, #foo)#foo 是一样的。

    注:目前主流浏览器中只有 Firefox 78+ 原生支持 :is(),见 MDN

  • :nth-child():nth-last-child()

    这两个伪类的特异性等于自身的特异性(B=1)加上参数中特异性最高的那个。如 :nth-child(even of li, .item) 的特异性是 (0,2,0)

  • :where()

    这个伪类的特异性为0。

    注:目前主流浏览器中只有 Firefox 78+ 原生支持 :where(),见 MDN

此外,处于存储空间的考虑,浏览器会对 A、B、C 的大小做一些限制,所以当他们的值大到一定程度后,就不会再增加了。

在某些说明中,会把内联样式(inline style)和 !important 也纳入到特异性的计算中,从而让他们也可以参与特异性的比较。这时只需要把是否有 !important (记为D)和是否为内联样式(记为E)加到 (A,B,C) 的前面,得到 (D,E,A,B,C),然后按顺序比较就可以了,越往后优先级越低。

如何查看特异性

在 Safari 浏览器中,我们可以通过开发者工具查看 CSS 选择器的特异性,如下图:

CSS Specificity

如何比较优先级

以下优先级依次增加:

  • 外部样式

    • 计算各选择器的特异性,依次比较 (A,B,C):A 不同的情况下,A 较大者优先级更高;A 相同的情况下,比较 B,依次类推。
  • 内联样式

    • 优先级高于普通的外部样式
  • !important

    • 当样式含有 !important 时,它的优先级高于普通的外部样式和内联样式。
    • 如果两个样式都含有 !important,则使用前面的规则决定优先级。
  • 如果上面所有的优先级比较后结果都是平局,则后来者居上。

较佳实践

至此,我们就可以推断出一段样式中到底哪一条才会赢到最后。也因为上面这些规则的存在,我们可以得出一些比较好的实践方式,来更轻松、更优雅地写 CSS:

  • 尽量不要用 !important,因为它打破了原本有秩序的优先级比较,强行开了一个绿色通道,使得它引入的新的样式很难再被其他样式覆盖。
  • 尽量不要用内联样式,因为不仅和 !important 类似,会让特异性的比较失效,还存在难以复用的问题。
  • 对于通用的样式,尽量使优先级更低,以便在需要小范围定制的时候,我们可以轻松地通过简单的约束,来产生优先级更高、代码依然优雅的样式。

这里再补充一个例子吧,ant-design 作为一个著名的 UI 框架,其样式代码就是一个典型的不想让开发者覆盖的例子。它的 CSS 代码几乎是按照 DOM 结构来嵌套的,这使得它的每个样式的特异性都非常高,如果我们要对它进行覆盖,几乎总是需要写一个比它更长的选择器。

这里并不是说它的代码写得不好,毕竟它的定位是一个设计方案而不是一个单纯的 UI 库。但是从普通开发者的角度来看,如果你想让你的 CSS 可维护性更高,更方便被定制,那就要尽量避免高优先级、高特异性的选择器,如果代码未来还是要自己来维护的,就给未来的自己留条路。


© 2020