最稳做法是直接用 &__element 和 &--modifier。因Less的&仅连接父选择器,仅&__element能确保编译为.block__element,语义清晰、可复用、支持组合;其他写法会破坏BEM作用域或脱离上下文。

直接用 &__element 和 &--modifier 写,别封装 Mixin,也别拼字符串——这是最稳、最易读、最符合 BEM 本意的做法。
为什么 &__element 是唯一安全的写法
Less 的 & 符号只是连接父选择器,不是“自动拼类名”的魔法。只有 &__element 能确保编译后是 .block__element,语义清晰、可复用、支持组合(比如 .block--featured .block__element)。其他写法都会出问题:
-
.block { .element { } }→ 编译为.block .element,破坏 BEM 作用域,变成后代选择器 -
.block { & .element { } }→ 同样是.block .element,权重飙升,后续覆盖困难 -
.@{block}__element { }→ 脱离当前作用域,生成平级选择器,无法继承块上下文
嵌套层级超过两层就是在埋坑
BEM 的核心是切断样式对 DOM 结构的依赖。一旦你写出 .block { .content { &__title { } } },编译结果是 .block .content .block__title,多出一个无意义的 .content 层级。这会导致:
- 调试时难以定位来源(三层嵌套 + 伪类 + 伪元素 = 五级选择器)
- HTML 结构微调,CSS 就得跟着改,违背组件化初衷
- 别人复用你的
.block时,发现它只在特定父容器下才生效
正确做法:所有 __ 和 -- 必须直属于块,不跨层。需要视觉分组?用文件拆分,而不是缩进嵌套。
修饰符必须绑定到块,不能挂在元素上
常见错误是写 .block__header--large,这违反 BEM 原则——修饰符描述的是「整个组件的状态」,不是某个子元素的尺寸。正确方式是:
-
.block--large .block__header:状态由块控制,元素样式响应变化 -
.block--loading .block__spinner:加载态影响多个子元素,统一开关 -
.block--no-shadow:整块关闭 box-shadow、border、hover 效果,不是单个元素
如果 modifier 只影响一个元素(如 .menu__item--active),那它是合法的,但前提是这个状态确实只属于该元素,且不会与其他 modifier 组合失效。
Vue 或 React 项目里,别让 Less Mixin 承担命名逻辑
有些方案试图用 .bem(card) 这类 Mixin 自动拼类名,但实际带来更大维护成本:
- IDE 无法识别动态生成的类名,失去语法高亮和跳转支持
- 搜索
card__header找不到定义位置,因为它是运行时拼出来的 - 修改块名要同时改调用点和 Mixin 定义,不如直接手写
.card { &__header { } }
真正该抽象的是 JS 层的类名组合逻辑(比如用 clsx),而不是 CSS 层的结构。Less 只负责把语义写清楚,不负责“自动化”。
最易被忽略的一点:BEM 不是字符串游戏,而是组件契约。类名写得再规范,如果 HTML 里把 user-avatar 当成 card__avatar 的子元素来用,整个结构就垮了——约束力不在 Less 语法里,而在团队对 block 边界的共识上。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











