浏览器层叠逻辑中权重相同时“后定义胜出”是解析顺序决定的必然结果,而非巧合;同权重规则按源码出现顺序排队,靠后者生效。

权重相同时“后定义胜出”是浏览器的层叠逻辑,不是巧合
这不是 CSS 故意设计的“时间偏见”,而是浏览器解析样式表时天然的处理顺序:它把所有规则收集完,按权重分组,再对同权重规则按源码中出现的先后顺序排队——靠后的自然排在队尾,最终生效。
这个行为叫「层叠(cascading)」,cascade 一词本意就是“像瀑布一样逐级落下”,而不是“随机挑选”。所以当 #header .nav a 和 .nav-link 权重都是 (0,0,1,1),浏览器不会抛硬币,而是直接取 HTML 中更靠后加载/定义的那个。
-
<style>.nav-link { color: blue; }</style>写在<link rel="stylesheet" href="base.css">后面 → 覆盖 base.css 里同权重的规则 - 两个
<link>标签,theme.css在reset.css后面 → theme.css 里同权重规则生效 -
@import声明虽写在<style></style>里,但实际解析晚于所有<link>→ 即使它语法上“在前面”,也大概率排在队尾
常见误判:你以为权重相同,其实差了一位
很多人看到 .btn 和 button.btn 都作用于同一个按钮,就默认它们权重一样,结果发现后者总赢——其实是没算准权重。
.btn 是 (0,0,1,0),button.btn 是 (0,0,1,1),仅多一个标签选择器,D 位就高 1,根本轮不到比顺序。
立即学习“前端免费学习笔记(深入)”;
- 检查是否混用了伪类:比如
a:hover(0,0,2,1) vs.link(0,0,1,0) —— 前者多一个伪类,权重已碾压 - ID 选择器带空格?
#user .profile是 (0,1,1,1),而#user.profile是 (0,1,1,0),前者多一个类选择器,权重更高 - 属性选择器也算 10:
[data-id]和.class权重相同,但[data-id].active就变成 (0,0,2,0)
引入顺序 ≠ 加载顺序,别被网络延迟骗了
你改了 main.css 末尾的样式却没生效,第一反应常是“是不是没加载到”?其实浏览器会等所有 CSS 解析完成才开始渲染,<link> 的 DOM 位置决定的是「解析后排序的依据」,不是「谁先下载谁先赢」。
- 用 DevTools 的 Elements 面板看目标元素,右侧 Styles 栏里被划掉的规则,点开小箭头能看到它被哪条规则覆盖、权重多少、来自哪个文件及行号
- 如果某规则显示 “
inherited from body”,说明它是继承来的,权重为 0 —— 即使写在最后也赢不了任何指定样式 -
@import在 2026 年基本已被弃用:它不仅权重最低((0,0,0,0)),还会阻塞后续 CSS 解析,导致 FOUC 或布局抖动
真正该警惕的,是 !important 和内联样式的隐性破坏
!important 看似是“终极覆盖开关”,但它只豁免单条声明,不提升选择器整体权重。更麻烦的是,它会让调试变得反直觉:你可能改了 5 个地方,最后发现某个 style="color:red !important" 在 DOM 上硬编码着,完全绕过所有外部样式表。
- 内联样式权重固定为 (1,0,0,0),能压倒任何 ID 组合(如
#app #header .title最高也就 (0,2,1,0)) -
!important在多人协作项目中极易失控:A 写了.btn { color: blue !important },B 为了覆盖又加一层.btn.primary { color: green !important },C 最后只能用 JS 强制移除 style 属性 - 现代框架(如 React/Vue)生成的动态 class 名、CSS-in-JS 注入方式,会让传统权重计算失效——此时得靠 DevTools 的 Computed 栏逆向查来源
权重相同才看顺序,这句话本身没错,但绝大多数“以为相同”的场景,其实权重早就不等了。真正在意顺序的时刻,往往出现在微调主题色、做 A/B 测试样式或维护遗留系统时——那时,你得盯紧 <link> 顺序、<style></style> 位置,以及有没有人偷偷往 DOM 上写了 style 属性。


















