先记住这个答案
在普通声明中,越靠后声明的层优先级越高。但当两条冲突规则都标了 !important 时,顺序完全反转:越靠前声明的层反而优先级越高。未分层样式在普通声明中最强,在重要声明中反而最弱。这是 CSS 层叠规范的设计,不是浏览器的怪异行为。
!important让层的优先级顺序反转- 先声明的层里的
!important最强 - 未分层的
!important反而最弱 - 设计目的是让底层重置不被覆盖
用一段代码看清反转
下面这段代码同时演示普通声明和重要声明的结果差异。
@layer base, theme;
@layer base {
.btn { color: gray; }
.alert { color: red !important; } /* 先声明的层 */
}
@layer theme {
.btn { color: blue; } /* 普通声明:theme 赢 */
.alert { color: green !important; } /* 重要声明:base 赢 */
}.btn 显示蓝色,因为 theme 层后声明,普通声明里它优先级更高。.alert 显示红色,因为两边都写了 !important,进入重要声明比较,先声明的 base 层反而赢了。
为什么规范要这样设计
想象一个场景:你在最底层的 reset 层里写了 * { box-sizing: border-box !important; },目的是不管上层怎么写都不能改掉这个行为。如果 !important 不反转顺序,上层随便写一个 !important 就能覆盖你的 reset,那这道防线就没有意义了。
反转之后,reset 层虽然普通声明优先级最低,但它的 !important 反而最强。上层的 !important 打不过底层的 !important,底层的保护性声明就真正「不可覆盖」了。
这也解释了为什么你应该尽量避免在高优先级层里写 !important。高优先级层的 !important 反而是最弱的,写了也没用。
未分层样式的 !important 最弱
在普通声明中,未分层样式比所有层都强。但在重要声明中反转了,未分层的 !important 比所有层的 !important 都弱。
!important 被层内 !important 覆盖CSS@layer base;
@layer base {
.tip { color: red !important; } /* 层内 !important,最终生效 */
}
/* 未分层 */
.tip { color: blue !important; } /* 未分层 !important,反而输了 */.tip 显示红色。这个结果和直觉完全相反,但规则很明确:重要声明里,层的优先级整个反转,未分层从最强变成最弱。
所以当你发现一个 !important 怎么都覆盖不了层里的 !important 时,先检查你的样式是不是写在层外面。如果是,把它也放进一个层里,并且放到更靠前声明的层里,才能赢。
容易答错的地方
- 以为
!important的行为和没有层时一样 - 没有层的时候,
!important只是提升优先级,后出现的!important赢先出现的。有了层之后,!important的比较跟随层的反转顺序,不再看源码先后。 - 搞混了「优先级高的层」和「重要声明更强的层」
- 普通声明里 utilities 层最强,重要声明里 utilities 层最弱。面试时直接记「
!important反转层序」这一句话就够了,然后推导出先声明的层里的!important最强。
面试官还会怎么问?
第三方库放到低优先级层里,它的 !important 反而更难覆盖?
对。如果库放在先声明的层里,库里的 !important 在重要声明排序中反而最强。解决办法是避免让库里带 !important,或者用 @layer 把库放到靠后声明的层里,让它的 !important 变弱。
层叠的完整排序是什么样的?
从低到高:普通声明中未分层最强,然后依次是后声明的层、先声明的层。重要声明中反转:先声明的层最强,后声明的层次之,未分层的 !important 最弱。行内 style 属性中的 !important 在所有重要层之上。
如果只有一边写了 !important,另一边没写呢?
写了 !important 的那边直接赢,不管层的顺序。反转只在两边都写了 !important 时才发生。一边有一边没有时,有 !important 的那条规则进入重要来源,没有的还在普通来源,重要来源整体优先于普通来源。
参考资料
示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。