CSS权重是四位数(a,b,c,d)逐位比较,不叠加不进位:a为内联样式个数,b为ID个数,c为类/属性/伪类个数,d为标签/伪元素个数;高位相等才比低位,高位大者胜出。

权重不是加法,是四位数比较
很多人误以为 #header .nav li 的权重是 100 + 10 + 1 = 111,再跟另一个 111 比大小——这是错的。CSS 权重本质是 (a, b, c, d) 四位独立数值,**不进位、不换算、不相加**,只从左到右逐位比大小。
比如:#main .item:hover 是 (0, 1, 2, 0),而 div#content p.text::before 是 (0, 1, 1, 2)。b 值都是 1,继续比 c:2 > 1,所以前者胜出——哪怕后者多一个标签和一个伪元素,d 位再大也无效。
-
style="color:red"→ (1, 0, 0, 0),永远压倒所有非内联规则 -
#nav a.active[href]→ (0, 1, 2, 1):1 个 ID、2 个类/属性/伪类(.active和[href])、1 个标签a -
ul > li:first-child→ (0, 0, 1, 2):伪类:first-child算 c,ul和li各算 1 个 d - 组合符
>、+、空格、~不参与计算,完全忽略
:not() 里的选择器才计权,:not 本身不算
这是最容易漏算的点。:not(.disabled) 整体不增加 c 值,但括号里的 .disabled 会正常计入 c 位。也就是说,它和单独写 .disabled 在权重上完全等价。
对比这两个选择器:
立即学习“前端免费学习笔记(深入)”;
-
button:not(.primary)→ (0, 0, 1, 1):括号内 1 个类,外面 1 个标签 -
button.primary→ (0, 0, 1, 1):同样 1 个类 + 1 个标签,权重完全相同 -
form :not([type="submit"]) input→ (0, 0, 1, 3):括号内 1 个属性(c+1),form、input和隐式后代关系中的元素不额外加 d,只算显式写出的标签
注意::is() 和 :where() 行为不同::is(.a, #b) 按其中**最高权重分支**计算(即按 #b 的 (0,1,0,0)),而 :where(.a, #b) 恒为 (0,0,0,0),强制降权。
!important 不改变权重,只改应用顺序
!important 不是“把权重调成 9999”,它绕过整个权重系统,属于独立裁决层。只要声明带 !important,就先于所有不带它的规则处理;如果多个都带,则**仍要回退到 (a,b,c,d) 比较**。
-
.box { color: blue !important; }和#app .box { color: red; }→ 前者生效(有!important,后者没有) -
.box { color: blue !important; }和#app .box { color: red !important; }→ 后者生效,因为 (0,1,1,0) > (0,0,1,0) -
style="color:green !important"是合法的,且优先级高于任何外部!important声明
浏览器 DevTools 里看到带删除线的样式,往往就是被更高权重的 !important 覆盖了,不是没加载。
权重相同时,后定义的才生效
当两个选择器的 (a,b,c,d) 完全一致,比如 .btn.success 和 .btn[aria-pressed="true"] 都是 (0,0,2,0),谁赢取决于它们在 CSS 中出现的**物理顺序**,而不是文件引入顺序或作用域位置。
- 同一 CSS 文件中,下面的规则覆盖上面的
- 多个
<link>引入时,后加载的 CSS 文件里定义的同权规则胜出 - 内联
<style>标签内的规则,按其在 HTML 中的位置判断先后 - 注意:CSS Modules 或 Vue scoped style 生成的属性选择器(如
[data-v-abc123])会人为拉高 c 值,导致看似“同名类”实际权重不同
真正难调试的,往往是那些权重相同、但来自不同构建产物或动态注入的样式——它们的“后定义”可能藏在你没注意到的 JS 注入逻辑里。


















