BEM通过block__element--modifier命名契约显性化样式所有权、切断隐式依赖,解决class名冲突与样式泄露;要求严格区分层级、禁用后代选择器和跨block复用,HTML中每个元素仅允许一个BEM class。

为什么 BEM 能解决 class 名冲突和样式泄露
项目越大会越明显:改一个 .header,结果侧边栏的 .header 也变了;组件复用时,子元素样式被父级 CSS 意外覆盖。BEM 的核心不是加长 class 名,而是靠命名契约切断隐式依赖——block__element--modifier 这种结构天然声明了作用域边界,浏览器不认“父子关系”,只认 class 字符串,所以只要命名唯一、结构扁平,就不存在“样式穿透”这回事。
常见错误现象:.card .title 这种后代选择器一加,.card 就不敢复用了;或者写成 .card-title 却没说明它是否属于 card 的一部分,后续维护者不敢动。
- 必须严格区分
block(独立组件)、element(block 的组成部分)、modifier(状态或变体) -
element名不能脱离block单独存在,比如.button__icon合理,.icon不合理 -
modifier只能修饰block或element,不能嵌套,如.button--disabled✅,.button__icon--hidden❌(应为.button--icon-hidden或拆出新 block)
CSS 预处理器里怎么写才不破坏 BEM 语义
用 Sass 写嵌套很容易写出反模式:.button { &__icon { ... } } 看似省事,但编译后仍是 .button__icon,问题不大;真正危险的是用 @extend 或 %placeholder 抽离公共样式时,把 .button--primary 和 .link--primary 共用同一个占位符,结果 class 名还是 BEM,实际样式逻辑已经耦合。
使用场景:组件库升级、老项目渐进式重构。
- 禁止
@extend跨 block 复用,哪怕样式相同——.button--small和.input--small必须各自定义 - 混入(
@mixin)只封装属性,不封装选择器,例如@mixin font-sm可以,@mixin button-base不行 - 变量名也要 BEM 化,比如
$button-padding-vertical,而不是$padding-sm
HTML 中 class 属性写法的硬约束
前端常以为 “class 多写几个没关系”,但在 BEM 下,多一个 class 就可能打破约定。比如给 button__text 同时加 text-center 和 button__text,后者语义明确,前者是全局工具类,混用会让团队无法判断哪些 class 是业务逻辑、哪些是通用辅助。
性能影响:过多 class 会略微增加 CSSOM 构建时间,但更严重的是可读性损耗——别人扫一眼 HTML,得花 2 秒分辨哪个 class 是 BEM 主干,哪个是临时加的“补丁”。
- 每个元素只允许出现一个 BEM class,即最多一个
block、一个block__element或一个block--modifier(或组合,如button button--large button__icon不合法,应为button button--large+button__icon) - 禁止使用非 BEM 的语义化 class,如
is-active、js-toggle——状态用block--active,行为绑定用 data 属性,如data-js="toggle" - 如果必须用工具类(如 Tailwind),只限于原型阶段,上线前必须转为 BEM class 或提取为 modifier
老项目迁移时最容易忽略的三件事
不是所有 class 都适合直接套 BEM。有些是纯布局类(.row、.col-6),有些是第三方库注入的(.swiper-slide),强行改会导致维护成本飙升。
容易踩的坑:用正则批量替换 .header → .page-header,结果把 JS 里 document.querySelector('.header') 也替换了,功能直接挂掉。
- 先用
grep -r '\.header' src/ --include="*.css" --include="*.js"梳理所有引用点,再决定是重命名、保留旧名、还是抽象为新 block - 第三方组件的 class 不要动,用 wrapper block 包一层,比如
,然后只对.chart-wrapper做 BEM 管理 - 构建时加 CSS lint 规则,比如 stylelint 的
selector-class-pattern插件,强制校验block(__element)?(--modifier)?格式,比人工 review 可靠
BEM 不是命名游戏,是把样式所有权显性化的过程。最复杂的从来不是怎么写,而是当某天发现一个 form__label 在 17 个文件里被不同方式复用时,你得立刻判断:它是该继续作为 element 存在,还是该升格为独立 block。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











