← 返回题目列表

CSS 选择器优先级怎么计算?

高频 中等 第 11 / 28 题 更新于 2026/07/27
CSS选择器优先级层叠

简化版

CSS 优先级按来源、重要性、选择器权重、书写顺序共同决定。选择器权重通常按内联样式、ID、类/属性/伪类、元素/伪元素比较,权重相同后写的覆盖先写的。

详细版

常见权重顺序:

  1. !important 会提高声明优先级,但仍受来源和层叠规则影响。
  2. 内联样式通常高于普通选择器。
  3. ID 选择器高于类选择器。
  4. 类、属性、伪类同级。
  5. 元素、伪元素较低。
  6. 通配符、组合符本身不增加权重。

例如:

#app .btn { color: red; }
.page .btn.primary { color: blue; }

第一条有一个 ID,通常会赢。实际项目中不建议滥用 ID 和 !important,否则样式很难覆盖。更好的方式是控制选择器复杂度,配合组件作用域、CSS Layers 或 BEM 命名。

完整版教学

一、CSS 层叠不是只看“谁更长”

很多人误以为选择器越长优先级越高,但真正比较的是权重类别。#id 的权重比很多类叠加还高,因为 ID 在更高的权重位。

可以粗略记成四元组:

内联样式, ID 数量, 类/属性/伪类数量, 元素/伪元素数量

比较时从左到右看,前一位大的直接胜出。

二、书写顺序什么时候生效

只有当前面的层叠来源、重要性、选择器权重都相同时,后出现的声明才覆盖先出现的声明。

.btn { color: red; }
.btn { color: blue; }

这里后者生效,因为权重完全一样。

但下面不一样:

#submit { color: red; }
.btn.primary { color: blue; }

即使第二条写在后面,ID 权重仍然更高。

三、:where 和 :is 的面试加分点

:where() 的优先级永远是 0,适合写基础样式而不增加覆盖成本。

:where(article h2) {
  margin-top: 2rem;
}

:is() 的优先级取决于参数里权重最高的选择器。现代 CSS 面试中提到这两个点,能体现你不仅会老规则,也关注新特性。

四、工程里的控制策略

大型项目里要避免选择器越写越重。比较健康的策略是:

  • 少用 ID 写样式。
  • 少用 !important,只在工具类或覆盖第三方样式时谨慎使用。
  • 组件样式保持浅层选择器。
  • 全局样式用低权重策略,组件样式负责具体表现。

这样样式系统才不会进入“为了覆盖而不断加权重”的泥潭。

五、面试追问与工程落地

优先级题进一步会问 !important 是不是最高。更准确的说法是:它会改变声明的重要性层级,但仍要放在完整层叠规则里比较,包括来源、cascade layer、选择器、顺序等。用户样式里的 important、浏览器默认样式、作者样式之间还有来源优先级差异。

现代 CSS 还引入了 @layer。层级靠后的 layer 会覆盖靠前 layer 中同等来源的样式,即使选择器权重较低也可能胜出。这让大型项目可以把 reset、基础样式、组件样式、工具类分层,减少靠加选择器权重解决覆盖问题。

工程里真正要避免的是“权重军备竞赛”。如果你发现需要 .page .panel .content .btn.primary 才能覆盖样式,通常说明样式边界设计有问题。更好的方案是降低全局选择器权重、组件化作用域、使用 CSS Modules 或 CSS Layers。

六、现场计算一次选择器权重

规范比较的是 ID、类类项、类型类项三个分量,并按字典序逐位比较,不存在“十个类进位成一个 ID”。内联样式属于层叠流程里的独立维度,用四元组帮助记忆可以,但不要把它误说成选择器语法本身的权重分量。

选择器权重 (ID, 类/属性/伪类, 类型/伪元素)说明
#app .btn(1, 1, 0)一个 ID、一个类
.page .btn.primary(0, 3, 0)三个类仍输给前者的 ID 位
button::before(0, 0, 2)一个类型、一个伪元素
:where(#app) .btn(0, 1, 0):where() 及其参数贡献为零
:is(.btn, #submit)(1, 0, 0)取参数列表中最高权重

若两个普通作者声明分别匹配 (1, 1, 0)(0, 20, 0),比较第一位就结束,前者获胜。只有来源、重要性、上下文、层叠层和权重等更前条件都相同时,才轮到作用域接近度与书写顺序。

面试中把 !important 直接写成“权重无穷大”不够准确;它改变的是声明的重要性层级,之后仍会在相应层级中继续执行完整层叠比较。

七、常见误区与追问

  • 误区:选择器写得越长,优先级就一定越高。 长度不是比较单位,关键是 ID、类类项和类型类项各自出现多少以及它们所在的层级。
  • 误区:十个类选择器可以抵消一个 ID 选择器。 各分量按字典序比较且不进位,只要 ID 分量更大,后续分量再多也不会翻转结果。
  • 误区::is():not():has() 自身都固定贡献一个伪类权重。 它们的权重由参数选择器列表中最高的权重替代,而 :where() 始终贡献零。
  • 追问:!important 是否能覆盖内联样式? 同一来源下的 important 声明会优先于普通内联声明,但不同来源、层叠层和动画过渡仍要按完整层叠顺序判断。
  • 追问:@layer 为什么能减少权重军备竞赛? 普通作者样式先按层顺序比较,后声明的层可在无需提高选择器权重的情况下覆盖前层。
  • 追问:同权重声明一定是后写的生效吗? 还要先保证来源、重要性、层、作用域等条件相同;这些都相同后,后出现的声明才胜出。
  • 追问:为什么基础样式适合用 :where() 它允许写有上下文约束的匹配条件却保持零权重,使组件样式更容易按正常层叠覆盖。

八、加强记忆

CSS 优先级的核心是“先分层,再比权重,最后看顺序”。不要用选择器长度判断胜负;真正要看 ID、类、元素分别有多少,以及是否有内联和 !important