先记住这个答案
层的声明顺序优先于选择器权重。两条冲突规则分别在不同层里时,浏览器先比层的顺序,后声明的层直接赢,根本不去看选择器写了多少层嵌套。选择器权重只在同一层内部才有用,用来决定层内多条规则谁赢。
- 跨层比较只看层的声明顺序
- 选择器权重只在同层内有效
- 未分层样式赢所有层
- 同层同权重时按源码顺序
一段代码验证层序碾压权重
下面的例子里,base 层用了三层嵌套的高权重选择器,theme 层只用了一个类选择器,结果是 theme 赢。
@layer base, theme;
@layer base {
#app .wrapper .btn { color: red; } /* 权重 (1, 2, 0) */
}
@layer theme {
.btn { color: blue; } /* 权重 (0, 1, 0),但赢了 */
}.btn 元素显示蓝色。theme 层后声明,优先级更高,层间比较时选择器权重不参与。这是 @layer 最核心的特性:用声明顺序取代选择器军备竞赛。
同一层内仍看权重和源码顺序
层间比较靠声明顺序,层内则和传统 CSS 一样。
@layer components {
.card .title { color: red; } /* 权重 (0, 2, 0) */
.title { color: blue; } /* 权重 (0, 1, 0) */
}两条规则在同一个层里,权重高的赢,.card .title 显示红色。如果权重也一样,就看谁写在后面,后面的赢。这和没有 @layer 时的行为完全一致。
所以 @layer 并没有废除选择器权重,它是在权重之上加了一层更高优先级的排序维度。
未分层样式在这个体系里的位置
没有放进任何层的样式,等价于排在所有命名层之后的一个隐含层,优先级最高。这意味着一条简单的 .btn { color: green; } 只要写在层外面,就能赢层里的任何规则,不管对方权重多高。
这个设计让渐进迁移变得安全:老项目可以先把第三方库用 @layer 包起来,自己的样式暂时不动,不会出现样式突然被覆盖的问题。等全部迁移到层之后,再用预声明统一管理顺序。
容易答错的地方
- 答成「权重高的赢」然后才提层
- 面试时如果先说权重,说明对层叠排序的理解还停留在没有
@layer的时代。正确的排序是:来源和重要性 → 层的顺序 → 选择器权重 → 源码顺序。层在权重之前。 - 混淆层的声明顺序和代码书写位置
- 有预声明时,层的优先级以预声明为准。base 层的代码写在文件最后面,优先级也不会变高,因为预声明已经锁定了它的排名。
面试官还会怎么问?
行内 style 属性和层的关系是什么?
行内样式不属于任何层,在层叠排序中优先于所有层和所有未分层样式。只有 !important 层声明能打败行内 !important,而且也要遵守反转规则。
同一层在多个文件里都写了,层内的源码顺序怎么算?
按文件被引入的顺序拼接。先引入的文件里的规则排在前面,后引入的排在后面。拼接完之后当作同一层内的一份代码来比较权重和源码顺序。
CSS Modules 或 Scoped Styles 和 @layer 冲突吗?
不冲突。CSS Modules 通过 hash 类名避免命名冲突,@layer 通过层的顺序控制优先级,两者解决不同的问题。你可以把 CSS Modules 产出的样式放进一个层里,同时享受两者的好处。
参考资料
示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。