@at-root #{&}__element是唯一安全生成BEM类名的方式,因&代表完整父选择器字符串,直接&__title会编译为.card .card__title(带空格后代选择器),违背BEM单类名、无DOM依赖原则。

SCSS里直接嵌套写&__element会编译出错误选择器
很多人以为.card { &__title { color: #333; } }能生成.card__title,实际编译结果是.card .card__title——带空格的后代选择器。这违背BEM“单类名、无DOM依赖”的核心,一旦HTML里漏掉.card父类,样式就失效。
真正安全的方式只有@at-root + 插值:@at-root #{&}__title { color: #333; },它强制跳出嵌套上下文,在CSS根级生成纯类名。
- 别在
@media、&:hover或子选择器内部用@at-root #{&}__xxx,输出顺序不可控,且破坏BEM层级语义 -
&代表的是当前完整选择器字符串,不是“块名”。在.button { .icon { &__spin { } } }里,&是.button .icon,结果变成.button .icon__spin,语义全乱 - 元素和修饰符必须与块同级定义,不能藏在状态或容器规则里
用@mixin封装BEM结构时,$block变量必须显式传入
靠&动态拼接只适用于单个块内,跨组件复用需靠变量控制命名空间。比如统一前缀xm-,就得声明$namespace: "xm",再用#{$namespace}-button生成xm-button。
常见错误是混用&和变量:@mixin b($name) { $B: #{$namespace}-#{$name}; .#{$B} { @content; } },这样写没问题;但若在里面又写&__icon,&就变成.xm-button,拼出来是.xm-button .xm-button__icon,又退回嵌套陷阱。
- 所有
__和--类名必须由@at-root提级生成,不能依赖嵌套语法 - 修饰符混入(如
@mixin m($mod))也得用@at-root #{&}--#{$mod},否则&可能携带多余上下文 - 避免在
@mixin里嵌套@mixin调用,容易让&指向失准
postcss-bem-linter只校验CSS写法,不检查HTML是否真用了类名
插件能立刻报出.button--loading写成.loading,或.button__icon-left漏了--,但它无法知道你JS里有没有动态拼class="button__icon button__icon--active"。如果HTML漏写button__icon基础类,只写了button__icon--active,样式照样失效,而插件完全沉默。
- 动态类名(如
class="card__item--${status}")必须在content配置里显式声明路径,否则PurgeCSS会误删 - 第三方组件(如
)若没在HTML里显式加class="van-button",插件和tree-shaking都识别不到 - BEM类名必须严格匹配
block__elem--mod格式,.card-item或.card--title这类变体不会被识别为有效组件类
修饰符不要暴露实现细节,--primary比--blue更可靠
--blue这种视觉描述类名,换主题时得全局搜索替换;--primary对应业务语义,只需改一次主题变量就能联动生效。更关键的是,--loading这类状态类容易和JS强耦合:JS设button--loading,CSS里又写.button--loading { opacity: 0.7; },结果设计师想把加载态改成骨架屏,你就得同时改JS逻辑和CSS规则。
- 禁止
--small、--left、--v2等不可枚举、不可预测的值 - 尺寸、颜色等应通过CSS自定义属性在
block层级统一定义,修饰符只表达有限组合的业务状态 -
button--disabled必须和button同时存在,单独用会导致构建工具无法识别上下文,tree-shaking失效
真正容易被忽略的是:BEM的解耦效果不来自语法糖,而来自对类名显式归属的强制约束。哪怕SCSS编译出的CSS完全正确,只要HTML里少写一个card或card__title,整个样式链就断了——这种问题在DevTools里只显示“未命中”,不会告诉你缺了哪个类。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











