CSS 选择器的权重怎么计算?
CSS 权重(Specificity)用四元组 (a, b, c, d) 表示,从左往右逐位比较,高位相同才比下一位:
| 位置 | 含义 | 示例 | 每个贡献值 |
|---|---|---|---|
| a | 行内样式 | style="" | 1 |
| b | ID 选择器 | #header | +1 |
| c | 类/属性/伪类 | .nav、[type="text"]、:hover | +1 |
| d | 标签/伪元素 | div、::before | +1 |
:::tip 通配符 * 、组合符(>、+、~、空格)不贡献权重。 :where() 始终零权重, :is() 取参数中最高权重参与计算。 :::
权重计算实战
逐个拆解选择器,把各部分归入对应位:
css/* (0, 1, 0, 0) */ #header { } /* (0, 0, 2, 1) — 2个类 + 1个标签 */ .nav .item a { } /* (0, 1, 1, 0) — 1个ID + 1个类 */ #sidebar .active { } /* (0, 0, 1, 2) — 1个伪类 + 2个标签 */ div p:hover { } /* (0, 0, 0, 0) — 通配符不贡献 */ * { }
比较规则:高位相同才比下一位。(0,1,0,0) 永远大于 (0,0,99,99) ——ID 列永远比 class 列大,不可进位。
!important 与权重的关系
!important 不参与四元组计算,它独立于权重体系之外:
shell优先级从高到低:!important > 行内 > ID > class > tag > *
当多条 !important 规则冲突时,回到权重比较决定谁胜出;权重也相同则后写的覆盖先写的。
css.box { color: red !important; } /* 同为 !important,回到权重比较 */ #box { color: blue !important; } /* #box 权重更高,blue 胜出 */
:::warning 滥用 !important 会导致样式不可维护,只能在覆盖第三方库等少数场景使用,并注释原因。 :::
:is() 与 :where() 的权重差异
这是面试高频追问点:
css/* :is() 取参数最高权重 */ :is(.nav, #main) a { } /* 等价于 #main a → (0, 1, 0, 1) */ /* :where() 始终零权重 */ :where(.nav, #main) a { } /* 等价于 a → (0, 0, 0, 1) */
:where() 的零权重特性非常适合写基础/重置样式——使用者用任意 class 即可覆盖,无需担心权重冲突。
@layer 对权重的影响
CSS Cascading Layers(@layer)引入了层叠层的概念,层的优先级规则:
- 无层样式 > 具名层样式(不管权重多高)
- 层内按声明顺序:后声明的层优先级更高
- 同一层内才按权重比较
css@layer base { #header { color: blue; } /* (0,1,0,0) 但在 base 层 */ } /* 无层 —— 即使权重低也会赢 */ .header { color: red; } /* (0,0,1,0) 无层,优先级更高 */
继承的权重
继承的样式没有权重,甚至低于通配符 *:
css* { color: gray; } /* (0,0,0,0) 但属于直接匹配 */ body { color: black; } /* 子元素通过继承得到 black */ /* p 元素会显示 gray —— 直接匹配 > 继承 */
实际项目中的权重管理策略
- BEM 命名:只用 class,杜绝 ID 和嵌套选择器,权重始终为
(0,0,1,0)级别 - 选择器嵌套不超过 3 层:
.block__element--modifier足够,避免.a .b .c .d - 用 :where() 写重置样式:
where(reset)可被任意 class 轻松覆盖 - CSS Modules / Scoped CSS:工具自动处理作用域,天然避免权重冲突
- 避免 !important:仅在覆盖第三方库样式时使用,务必注释原因
追问
多个类选择器和一个 ID 选择器哪个权重高?
ID 权重更高。#foo 是 (0,1,0,0),.a.b.c.d.e 再多类也是 (0,0,5,0)——高位相同才比下一位,b 列 1 > c 列 5,ID 永远胜出。
怎么快速判断两个选择器的优先级?
分三步:(1) 先看有没有 !important;(2) 再看是否在 @layer 中(无层 > 有层);(3) 最后按四元组从左往右逐位比。同权重时后写的覆盖先写的。
内联样式和 !important 谁优先?
!important 优先。!important > 行内样式 > ID > class > tag。但两条都是 !important 时,再回到权重体系比较。
伪类 :not() 的权重怎么算?
:not() 本身不贡献权重,但它括号内的选择器参与计算。:not(.foo) 的权重等于 .foo,即 (0,0,1,0)。同理 :not(#bar) 按 ID 计算权重 (0,1,0,0)。注意 :not() 内可以写复杂选择器,取其完整权重。