应将嵌套深度限制为3层,因超深嵌套生成冗余长链选择器(如.page .main .content .card .title),导致调试困难、DOM变动失效、被迫使用!important;可通过stylelint-config-sass-guidelines配置max-nesting-depth: 3自动拦截,并结合BEM命名、@at-root跳出、子选择器>等手段优化。

用 stylelint-config-sass-guidelines 配置 max-nesting-depth
直接在项目中加一条规则,就能让嵌套超深的代码在保存时就报错,而不是等上线后才发现样式失效或 DevTools 里找不到规则。
- 安装依赖:
npm install --save-dev stylelint stylelint-config-sass-guidelines - 在
.stylelintrc.js或.stylelintrc.cjs中启用:module.exports = { extends: ["stylelint-config-sass-guidelines"], rules: { "max-nesting-depth": 3, "no-duplicate-selectors": true } }; -
max-nesting-depth: 3指的是编译后 CSS 中连续空格分隔的选择器层级(如.card .header .title算 3 层),不是 Sass 源码里缩进的层数 - 注意:该规则对
@media、@supports块内的嵌套不计数,只检查选择器链本身
嵌套层级怎么算才准?别被 & 搞晕
很多人以为缩进几层就是几层,结果 .btn { &__icon { &::before { } } } 看似三层,编译后却是 .btn__icon::before——只算 1 层。真正要盯的是最终 CSS 里空格拼接的 class 数量。
- 写完 Sass 后,打开编译出的 CSS 文件,找类似
.page .main .content .card .title这种带 4 个及以上空格的规则,它就是失控信号 -
&在不同上下文拼出的结果不同:.card { &__title { } }→.card__title;但.card { .title { &--large { } } }→.title--large(&此时指向.title,不是.card) - 用
stylelint的selector-max-compound-selectors规则可辅助检测长选择器链
超过 3 层时,删最内层比补 & 更有效
遇到 .modal .overlay .backdrop .fade .in 这种五层嵌套,第一反应不该是“加个 & 让它更精确”,而是立刻砍掉一到两层——因为浏览器匹配是从右往左的,.in 越靠右,性能越差,且 DOM 结构稍变就全挂。
- 优先转成 BEM 类名:
.modal__backdrop--fade-in,单类名,权重低,复用强 - 用
@at-root提前跳出嵌套,比如全局状态body.is-loading .spinner就不能写在.spinner块里 - 把重复结构抽成
@mixin,比如@mixin text-variant($color),而不是靠.text { &--success { &.is-bold { } } }套娃 - 子选择器
>比后代空格更可控,.list > .item不会意外匹配到深层嵌套的.item
原生 CSS 嵌套(@nest)不能替代 Sass 限制
原生 @nest 是运行时特性,不解决构建期嵌套失控问题,而且语法限制极多——它不支持 &:hover &::before 这种组合,也不允许嵌套 @nest 块本身。
立即学习“前端免费学习笔记(深入)”;
- Chrome 119+、Safari 17.4+ 才支持
@nest,旧版浏览器静默忽略,毫无降级提示 -
@nest & .active合法,但@nest &.active(漏空格)或@nest .active &(& 不在开头)直接报错 - 想兼容 IE 或早期 Edge?必须走 Sass 预处理路线,
@nest只能作为渐进增强补充,不能当主力
& 时都得问一句:这个拼出来的选择器,是不是真需要那么高权重?是不是挪个父容器就失效?是不是 DevTools 里一眼能找到它?


















