CSS 优先级与特异性

解释特异性如何参与层叠比较,纠正“行数靠后一定赢/权重 100/10/1 算术”的简化误区,并给出工程中可维护的优先级策略。

#type / concept #status / growing #tech / dev / frontend #resource / css

[!info] 关联笔记

CSS 优先级与特异性

这个概念为什么出现

同一元素被多条规则命中时,浏览器必须决定用哪一条声明。若团队不懂特异性,就会陷入:

  • 不断加更长选择器“压过去”
  • 滥用 !important
  • 改一处样式,三处布局炸裂

特异性(specificity)是层叠比较中的关键维度之一。

[!abstract] 一句话理解 特异性衡量选择器有多“具体”;在来源与层叠层相同的前提下,更具体的选择器胜出,再相同才看源顺序。

最小可运行示例

场景:库存状态徽章被全局样式误伤

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="utf-8" />
  <title>specificity lab</title>
  <style>
    /* A: 元素选择器 */
    span { color: black; }

    /* B: 类选择器 */
    .badge { color: #334155; }

    /* C: 双类 */
    .badge.badge--danger { color: #b91c1c; }

    /* D: ID(演示用,业务中应谨慎) */
    #sku-badge { color: #0369a1; }
  </style>
</head>
<body>
  <span id="sku-badge" class="badge badge--danger">低库存</span>
  <script>
    const el = document.getElementById('sku-badge')
    console.log(getComputedStyle(el).color)
  </script>
</body>
</html>

建议运行:浏览器打开控制台。

期望:

计算色来自 #sku-badge(ID 特异性通常高于 class)
若去掉 id,则 .badge.badge--danger 胜于 .badge 与 span

结合场景关注点

  1. 不是“写在后面就一定赢”,要先比特异性。
  2. ID 很容易把后续覆盖成本抬爆。
  3. 组件变体用 class 组合,比 #id 更可维护。

核心概念与准确模型

比较顺序(工程压缩)

层叠比较通常先看更宏观的条件(来源、层叠层、是否 important 等),再在同一层级内比较特异性,最后才是源顺序。完整算法以 CSS Cascading and Inheritance 为准。

特异性三元组(教学模型)

把选择器记为 (A, B, C)

分量计入
AID 选择器数量
Bclass、属性、伪类数量
C类型选择器、伪元素数量

比较时从左到右,不是把 100/10/1 当十进制真算术(这是常见教学简化,遇到多位数会误导)。

不计入:

  • 通用选择器 *
  • 组合符( > + ~
  • :where() 的特异性为 0(现代好工具)
  • 内联 style 属性具有比选择器更高的一档(另记)

!important

它改变的是声明在层叠中的优先级档位,不是“特异性 +∞ 的健康写法”。组件库与工具类冲突时尤其危险。

设计动机

CSS 允许多样式来源叠加(用户代理、作者、用户)。特异性让“更具体的意图”能覆盖“更一般的默认”,同时源顺序处理同级冲突。

边界与反直觉

  • 属性选择器 [id=x] 的特异性按属性选择器计,不等于 #x
  • 伪类 :not() 的特异性由其参数贡献(注意与 :where 差异)。
  • 继承来的值与“选中元素的声明”比较方式不同:被继承属性没有特异性去打直接命中的声明。

常见误区

[!warning] 常见误区:用 100/10/1 做十进制加法 应逐列比较计数。11 个 class 不会进位变成 1 个 ID。

[!warning] 常见误区:靠 !important 终结战争 只会把战争抬到 important 层,维护成本更高。

工程实践

  1. 保持特异性平坦:BEM、CSS Modules、utility 都在降选择器深度。
  2. 组件状态用 class.is-active / .badge--danger
  3. 避免 ID 选择器写样式(锚点 ID 可以有,样式别绑)。
  4. 调试用 DevTools 看被划掉的规则与 computed。
  5. Tailwind/Modules 冲突时先定位层叠层与顺序,再谈配置。

可验证实验

  1. 同特异性两条规则,调换源顺序,观察胜者变化。
  2. 给败者加 !important,观察“打赢”的代价。
  3. :where(.badge) 包一层,看特异性归零对覆盖的影响。

本节总结

特异性是层叠的标尺之一。目标不是赢得单次覆盖,而是让团队在低特异性下可预测地工作。

自测题

概念题

  1. 为什么说 100/10/1 是简化?
  2. 内联 style 与 #id 谁更“硬”?

代码推理题

  1. .a.b#c 同时命中且都无 important,谁赢?
参考答案
  1. 真实比较是多列计数,不会像十进制那样进位。
  2. 内联 style 档位通常高于 ID 选择器(除非 important 等更复杂层叠情况)。
  3. #c 的 A 分量更大,通常胜出。

延伸阅读

资料类型支撑内容
CSS Cascade 5规范层叠与特异性
MDN Specificity文档计算示例
MDN Cascade文档层叠全流程
创建于 2025/1/1 更新于 2026/7/15