BEM在Less中必须显式使用&拼接类名,漏写&会导致后代选择器而非连缀类名;modifier应封装为参数化mixin;禁止多层嵌套滥用&或字符串插值生成类名。

Less里写BEM,&必须显式出现才能拼出正确类名
漏写 & 是新手最常踩的坑:写成 __text 会编译成后代选择器 .button .text,而不是预期的 .button__text。BEM 的原子性依赖类名连缀,一旦变成空格分隔,就失去独立性、可复用性和低权重优势。
正确写法只有一条铁律:&__element 和 &--modifier 必须带 &,且紧贴前面,中间不能有空格或换行干扰。
-
.button { &__icon { ... } }→ 编译为.button__icon -
.button { __icon { ... } }→ 编译为.button .icon(破坏BEM) -
.button { & --primary { ... } }→ 编译为.button --primary(非法选择器)
多个Block共用Modifier时,用参数化mixin封装而非硬编码
当 .button--disabled、.input--disabled、.card--disabled 都需要同一套样式逻辑,重复写 &--disabled 不仅冗余,还难统一维护。
此时应抽离为带参数的 mixin,利用 & 在调用上下文中动态绑定:
立即学习“前端免费学习笔记(深入)”;
.bem-disabled() {
&--disabled {
opacity: 0.5;
pointer-events: none;
}
}
.button {
.bem-disabled();
}
.input {
.bem-disabled();
}
注意:这个 mixin 不能带前缀类名(如 .disabled),否则会生成独立选择器;它必须依赖 & 在父作用域内展开,才真正符合 BEM 的语义边界。
禁止在嵌套中滥用 & 导致选择器爆炸
多层嵌套 + 错位 & 会产出超长、高权重、不可调试的选择器,比如:
.card {
.header {
&__logo { ... }
}
}
编译结果是 .card .header .card__logo —— 中间多出一个无意义的 .header,既提高权重,又让开发者误以为 .card__logo 依赖 .header 结构。
正确做法是扁平化层级,所有元素都直属于 block:
-
.card { &__header { } &__logo { } &__body { } }✅ -
.card { .header { &__logo { } } }❌(引入非BEM结构依赖) -
.card { &__header { &:hover { &__title { } } } }❌(四层嵌套,调试时根本找不到来源)
别用字符串插值生成BEM类名,手写+&才是唯一可控路径
有人试图用 @{block}__title 动态拼类名,但 Less 不支持运行时类名解析,这种写法会让 IDE 无法跳转、linter 失效、CSS Modules 下 hash 错乱,且违反 BEM “静态契约”本质。
BEM 类名本就是人工约定的接口,不是需要计算的变量。真正该封装的不是“怎么拼”,而是“哪些 modifier 可组合”、“哪些 element 允许被覆盖”:
- 直接手写
.button { &__text { } &--primary { } }是最清晰、最易查、最兼容工具链的方式 - 如果真要抽象,只封装行为逻辑(如
.bem-hover()),不封装类名结构 - Vue/React 中动态类名统一走
clsx或封装函数(如cnButton({ variant: 'primary' })),不在 Less 层做字符串拼接
复杂点在于 modifier 的组合约束——比如 .button--primary.button--large 合法,但 .button--primary--large 违反 BEM,而 Less 本身不校验这个,全靠人盯和 lint 配置。这点最容易被忽略。


















