BEM强制使用block__element和block--modifier是为了切断样式对DOM结构的依赖,确保类名仅表达语义而非层级关系;块名须小写短横线分隔且与组件名映射,修饰符仅用于明确业务变体,避免滥用纯表现型命名。

为什么BEM的class名里必须用双下划线和双破折号
BEM强制用 block__element 和 block--modifier 是为了在CSS选择器层面彻底切断样式继承与嵌套联想。浏览器不认“父子关系”,只认class字符串是否匹配——一旦写成 header-nav-item 或 header__nav__item,你就默认引入了结构依赖,后续改HTML标签或移动DOM位置时,样式就容易意外失效。
常见错误现象:.card .title 看似简洁,但只要某处把 h2.title 换成 div.title,或者把 .title 提到卡片外,样式立刻断掉;而 .card__title 完全不关心它是不是子元素、有没有父容器,只靠类名本身表达语义。
- 双下划线
__专用于连接块与元素,表示“这个东西属于那个块”,不是视觉层级 - 双破折号
--只用于修饰符,比如button--primary、button--disabled,不能写成button-primary(会和别的块冲突) - 不允许三层命名,如
form__field__input是错的——form__input才对,元素必须直接隶属于块
如何让BEM不和React/Vue组件名重复打架
很多人在JSX里写 className="button",又在CSS里定义 .button__icon,结果全局污染、样式穿透、热重载错乱全来了。BEM不是独立于组件体系存在的,它得和组件边界对齐。
使用场景:现代框架中,每个组件应有唯一且稳定的块名前缀,比如 ProductCard 组件对应 product-card 块,所有内部class都从它出发:
立即学习“前端免费学习笔记(深入)”;
.product-card { }
.product-card__image { }
.product-card--featured { }
关键点在于——块名必须小写、短横线分隔、和组件文件名/导出名可映射,不能用驼峰或下划线。
- 禁止用组件变量名直接当块名,比如
const UserProfile = () => {...}不能对应userProfile块(大小写混用+无分隔符) - Vue单文件组件中,建议在
<style scoped>外再加一层data-v-xxx不是目的,真正要靠BEM命名本身隔离作用域 - 如果用了CSS Modules,仍建议保留BEM结构,只是把
product-card__title写成styles.title——命名逻辑不变,只是运行时哈希化
遇到伪类、响应式、主题切换怎么加BEM修饰符
BEM修饰符不是用来塞CSS状态的垃圾桶。它只表达“这个块/元素在某种明确业务语义下的变体”,而不是“hover时变色”这种表现层细节。
常见错误现象:有人写 button--hover、card--md、input--dark,结果发现这些修饰符根本没法被JS稳定控制,也难以测试和复用。
-
button--disabled合理,因为它是组件API的一部分,由disabledprop 控制,语义清晰、可预测 -
button--loading合理,对应明确的状态机阶段,能被自动化测试覆盖 - 避免
button--small这类纯尺寸修饰符,应该归入设计系统Token,用CSS自定义属性驱动,比如--button-padding: var(--size-xs) - 媒体查询相关样式不要进修饰符,而是用独立的选择器,如
@media (min-width: 768px) { .product-card__content { ... } }
为什么BEM不能解决所有样式耦合问题
BEM管得住class命名,但管不住CSS声明本身的副作用。比如你在 .modal__overlay 里写了 position: fixed + z-index: 9999,这个z-index就可能和另一个 .toast--top 冲突,命名再规范也没用。
性能影响常被忽略:大量使用 block__element--modifier 类名会导致HTML体积上升,尤其服务端渲染场景下,首屏HTML中class字符串占比可能超15%。这不是BEM的错,但你要意识到它带来的真实开销。
- 修饰符数量别超过5个/块,否则说明语义没收敛,该拆组件了
- 不推荐用BEM组织工具类(如
text-center、mt-2),那是Utility-First的事,混用反而增加认知负荷 - 最易被忽略的一点:BEM要求每个块独立可复用,但现实中很多“块”其实依赖上下文(比如暗色模式需父级
theme-darkclass),这时候得靠CSS Nesting或自定义属性透传,而不是硬塞修饰符


















