@at-root 必须直接作用于目标选择器本身才能生效,如 .modal { @at-root .is-loading { ... } } 才能编译为独立规则;误嵌套、加前导选择器或在 @media 中不加 (without: all) 均导致失效。

为什么 @at-root 没让选择器真正“跳出”嵌套
最常见的情况是:你在 .modal 块里写了 .is-loading { @at-root .is-loading { opacity: 0.5; } },结果编译后还是 .modal .is-loading。这不是 @at-root 失效,而是它根本没被触发——@at-root 必须**直接作用于目标选择器本身**,不能套在另一个嵌套规则内部再用。
它只认这一种写法:.modal { @at-root .is-loading { opacity: 0.5; } }。此时 .is-loading 才会脱离 .modal 上下文,编译为独立的 .is-loading 规则。
- 误加前导选择器(如
@at-root .modal .is-loading)会让它仍被包裹,失去提级意义 -
&在@at-root内部无效,比如.card { @at-root &__header { ... } }会报错 - 想批量提级多个类,要用块语法:
@at-root { .is-loading { ... } .is-disabled { ... } }
@at-root (without: all) 才能真正脱离 @media 和组件作用域
默认 @at-root 只跳出选择器嵌套,对 @media、@supports、@layer 完全“视而不见”。你在 @media (min-width: 768px) 里写 @at-root .theme-dark,编译结果仍是该媒体查询内的 .theme-dark——小屏下这个类根本不会生效。
要让它变成全局可用、不受任何上下文限制的类,必须显式写:@at-root (without: all) .theme-dark { ... }。
-
@at-root (without: media)只跳出媒体查询,但还卡在父选择器里 -
@at-root (without: rule)是默认行为,等价于裸@at-root -
with:后只能跟media、rule、all,写成with: screen会直接报错
在 @each 或 @mixin 中用 @at-root 的两个隐形陷阱
变量作用域和语句顺序会悄悄破坏你的预期,尤其在封装主题或批量生成工具类时。
比如这段代码能跑通:
@each $name, $color in $themes {
.#{$name} {
@at-root .theme-#{$name} { --primary: #{$color}; }
}
}
但如果改用 @let $name: ...(Sass 1.50+),@at-root 内部就访问不到该变量——它不在当前作用域链上。
- 不能在
@extend或@include后直接跟@at-root,Sass 不允许这种语句顺序 - 在
@mixin里定义的局部变量(如$size: 16px),放进@at-root (with: media)后就不可见了 - 别指望
@at-root解决 Vue scoped CSS 穿透问题;它生成的是真实全局类名,可能污染其他组件
什么时候不该用 @at-root 而该换思路
@at-root 是控制 Sass 编译时选择器生成位置的指令,不是运行时样式穿透方案。它不解决 Shadow DOM 或 scoped CSS 的隔离问题。
例如在 Vue 单文件组件中写 .parent { @at-root .child { ... } },意图替代 :deep(.child),这是危险误用:前者生成全局类,后者由 Vue 编译器注入作用域逻辑。
- 真要穿透,优先用
:deep(.child)或::v-deep .child - BEM 场景下慎用
&+@at-root组合,容易产出.block--theme-dark .block__text这类依赖 DOM 结构层级的选择器,实际部署时易失效 - 如果只是想降低 specificity,与其硬用
@at-root提级,不如重构嵌套结构或改用更扁平的类命名
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











