CSS特异性控制:层叠层 vs BEM vs 实用类

本文深入探讨了CSS特异性控制的三种主要方法:BEM命名规范、实用类优先策略和CSS层叠层技术,分析了各自的优劣及适用场景,帮助开发者选择最适合项目的样式管理方案。

CSS层叠层 vs BEM vs 实用类:特异性控制

CSS是狂野的,非常狂野。而且很棘手。但今天我们特别要讨论的是特异性问题。

特异性基础

当编写CSS时,几乎不可能没遇到过样式未按预期应用的情况——这就是特异性。你应用了一个样式,它生效了,后来你尝试用不同的样式覆盖它时…毫无反应,它直接无视了你。还是特异性问题。

当然可以使用!important标志,但像所有前辈开发者一样,这总是有风险的且不被鼓励。完全理解特异性比走这条路要好得多,否则你最终会和自己重要的样式作斗争。

特异性101

许多开发者以不同方式理解特异性的概念。特异性的核心思想是浏览器使用的CSS层叠算法,当两个或多个规则匹配同一元素时决定应用哪个样式声明。

随着项目扩展,特异性挑战也随之增加。假设开发者A添加了.cart-button,然后按钮样式看起来可以用于侧边栏,但需要微调。后来开发者B添加了.cart-button .sidebar,从此之后,任何对.cart-button的更改都可能被.cart-button .sidebar覆盖,特异性战争就此开始。

不同策略对比

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
/* 传统方法 */
#header .nav li a.active { color: blue; }

/* BEM方法 */
.header__nav-item--active { color: blue; }

/* 实用类方法 */
.text-blue { color: blue; }

/* 层叠层方法 */
@layer components {
  .nav-link.active { color: blue; }
}

所有这些方法反映了控制或至少维护CSS特异性的不同策略:

  • BEM:通过显式命名简化特异性
  • 实用优先CSS:通过保持原子性来绕过特异性
  • CSS层叠层:通过分层组织样式来管理特异性

BEM:原始系统

BEM(Block-Element-Modifier)已经存在很长时间了。它是一个编写CSS的方法论系统,强制你明确每个样式层次结构。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
/* 块 */
.panel {}

/* 依赖于块的元素 */
.panel__header {}
.panel__content {}
.panel__footer {}

/* 修改块样式的修饰符 */
.panel--highlighted {}
.panel__button--secondary {}

BEM如何处理特异性

1
2
3
4
5
6
7
8
9
/* 无BEM - 特异性: 0,3,0 */
.site-header .main-nav .nav-link {
  color: #472EFE;
}

/* 有BEM - 特异性: 0,1,0 */
.main-nav__link--special {
  color: #FF5733;
}

BEM使代码看起来可预测,因为所有选择器都是平等的,使代码更易于维护和扩展。

BEM的不足

  • 类名可能变得非常长
  • 可能不优先考虑可重用性
  • 命名事物成为现实中的痛点

实用类:通过避免处理特异性

这也称为原子CSS。本质上它避免了特异性问题。

1
2
3
<button class="bg-red-300 hover:bg-red-500 text-white py-2 px-4 rounded">
  一个按钮
</button>

实用优先类的理念是每个实用类具有相同的特异性,即一个类选择器。每个类都是一个具有单一目的的微小CSS属性。

实用类如何处理特异性

实用类不解决特异性,而是将BEM的低特异性理念发挥到极致。几乎所有实用类都具有(0,1,0)的最低特异性级别。

实用类的权衡

  • 使代码看起来"丑陋"
  • 存在重复问题
  • 全局更改困难
  • 破坏了自然的父子关系

层叠层:通过设计处理特异性

BEM提供结构,实用类获得速度,而CSS层叠层给了我们最重要的东西:控制。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
@layer utilities, defaults, components;

@layer defaults {
  button {
    background-color: orange; /* 特异性: 0,0,1 */
  }
}

@layer components {
  .button {
    background-color: blue; /* 特异性: 0,1,0 */
  }
}

@layer utilities {
  #button {
    background-color: red; /* 特异性: 1,0,0 */
  }
}

由于@layer的工作方式,.button会胜出,因为components层是最高优先级的,即使#button具有更高的特异性。

层叠层的细微差别

  • 特异性仍然是游戏的一部分
  • !important@layer中的行为与预期不同(它们反向工作!)
  • @layers不是选择器特定的,而是样式属性特定的
  • @layer容易被滥用

三者比较

特性 BEM 实用类 层叠层
核心理念 命名空间组件 单一目的类 控制层叠顺序
特异性控制 低且平坦 完全避免 由于层优先级而绝对控制
代码可读性 命名清晰的结构 如果不熟悉类名则不清晰 如果遵循层结构则清晰
HTML冗长度 中等长度类名(可能变长) 许多小类累加 无直接影响,仅存在于CSS中
最佳用例 设计系统 快速构建 需要覆盖的遗留代码或第三方代码

结论

在比较BEM、实用类和CSS层叠层时,是否有一种真正的"获胜"方法来控制层叠中的特异性?

首先,CSS层叠层可以说是多年来我们获得的最强大的CSS功能。它们不应该与BEM或实用类混淆,后者是策略而不是CSS功能集的一部分。

这就是为什么我喜欢将BEM与层叠层结合,或者将实用类与层叠层结合的想法。无论哪种方式,理念都是保持低特异性,并利用层叠层为这些样式设置优先级。

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计