Less中&后有无空格决定输出为并列类名(如.btn.modifier)或后代选择器(如.btn .child),空格缺失或多余会导致编译错误或语义错误,BEM扁平输出必须严格控制拼接逻辑。

用 & 拼接类名时,空格决定是扁平还是嵌套
Less 编译器对 & 后是否带空格极其敏感——它不猜意图,只做字符串拼接。&.modifier 输出 .btn.modifier(并列类,权重低);& .child 输出 .btn .child(后代选择器,权重高且匹配慢)。很多人写 &:hover 没问题,但一写成 & :hover(中间多空格),Less 就报 Unknown word 错误,因为解析成了标签选择器。
常见踩坑点:
-
.card { .header { color: red; } }→ 编译为.card .header,不是.card__header -
.card { &__header { } }→ 正确生成.card__header,这才是 BEM 扁平输出 - 想让按钮禁用状态和图标样式共存,别写
.btn.is-disabled { .icon { } },改用.btn { &.is-disabled { &__icon { } } },否则编译出的选择器是.btn.is-disabled .icon,权重翻倍还难覆盖
三层以上嵌套会直接拉高选择器权重和渲染开销
浏览器匹配 .a .b .c .d 是从右往左逐级回溯的,DOM 越深、节点越多,样式计算耗时越长。实测显示,嵌套超三层后,低端安卓 WebView 中样式计算耗时上升 40% 以上。更麻烦的是权重:四层嵌套生成的选择器权重是 0,0,4,0,而 .card__title 只有 0,0,1,0,后者用 .card__title:hover 就能轻松升级,前者可能得靠 !important 硬顶。
哪些写法实际等于埋雷:
立即学习“前端免费学习笔记(深入)”;
-
.modal { .overlay { .content { h1 {} } } }→ 编译为.modal .overlay .content h1 -
@media (max-width: 768px) { .nav { li { a {} } } }→ 每个a规则都重复带前缀,体积和权重双膨胀 -
.btn { &:hover { .icon {} } }→ 编译为.btn:hover .icon,权重0,0,2,1,远高于.btn--hover .btn__icon
媒体查询和伪类里漏掉 & 就等于全局污染
@media 或 :hover 块内如果不显式用 & 绑定父级,Less 就不知道你要作用于谁。比如写 .nav { @media (max-width: 768px) { a {} } },编译结果是全局 a 标签样式,不是 .nav a —— 因为没 &,Less 默认当新规则处理。
必须这么写才安全:
-
@media (max-width: 768px) { .nav { & a {} } }→ 输出@media (max-width: 768px) { .nav a {} } -
.link { &:hover { color: blue; } &::before { content: ""; } }→&后紧跟冒号,中间不能有空格 -
.dropdown { &:hover { &__menu { display: block; } } }→ 状态与子元素绑定清晰,不依赖 DOM 深度
@at-root 是跳出嵌套、生成同级选择器的唯一可控方式
当你需要一个和当前块平级的类(比如修饰符或工具类),又不想让它被父选择器包裹,@at-root 是唯一可靠手段。它不是语法糖,而是明确告诉 Less:“这条规则别拼前缀,原样输出”。比如 .card { @at-root .card--compact { padding: 4px; } } 编译后就是独立的 .card--compact,权重恒为 0,0,1,0。
替代方案往往更危险:
- 用变量拼接类名:
.@(concat(@block, '--compact'))→ 依赖 Less 4+,兼容性差,IDE 不识别,lint 工具报错 - 把修饰符写在元素上:
.card { &__title--large { } }→ 违反 BEM 规范,无法响应.card--featured等组合状态 - 手动提级写
.card--compact { ... }→ 容易漏维护,和.card的逻辑割裂,重构时难追溯
&,而是每次敲下 { 前得判断:这个结构是给人看的语义分组,还是给机器执行的精确命中?前者可以缩进,后者必须扁平。


















