BEM 是解决类名冲突与样式覆盖的根本方案,通过 block__element--modifier 结构明确归属、层级和状态,杜绝隐式 DOM 依赖与全局污染。

类名冲突或样式覆盖已频繁发生
当你在 DevTools 里看到同一个 .logo 类被 reset.css、header.scss、modal.less 和第三方组件同时定义,且改一处导致页头、弹窗、侧边栏全变形,说明项目已越过临界点。这不是偶然,是命名没回答“属于哪个模块、当前什么状态”,浏览器和人都得靠猜。
常见现象包括:
-
.card .title这类后代选择器散落在多个文件中,DOM 结构一动,样式直接失效 -
is-mobile被硬塞进按钮、表单、弹窗,最后变成全局开关,改断点要grep -r "min-width"半小时 - 审查元素时发现
.button在登录页是圆角,在支付页是直角,但 CSS 文件混在一起引入,没人敢动
组件复用超过 3 个模块或跨子应用引用
当 UserCard、SearchForm、ProductGrid 开始被多个页面甚至微前端子应用引用,类名就必须自带上下文。否则 .button 在不同场景含义不同,却共享同一份样式源,协作成本会指数级上升。
判断标准很实在:
立即学习“前端免费学习笔记(深入)”;
- 删掉父容器后,这个类名还能独立存在并表达完整语义?能 → 它该是 Block;不能 → 它是 Element
-
.profile-card__avatar合理,因为头像脱离卡片通常不复用;.logo若在页头、页脚、弹窗都用同一套样式,它自己就是 Block,不该写成.header__logo - 第三方组件如
ant-select不是你项目的 Block,硬套form__ant-select会破坏封装和升级路径
SCSS/Less 中出现带空格的嵌套编译结果
检查构建产物:grep -r "\.[a-z]\+ \.[a-z]" dist/。只要命中,就代表存在 .card .title 这类选择器——它让浏览器从右往左逐层回溯父节点,DOM 越深越卡,且无法被 tree-shaking 清理。
根本原因常是预处理器误用:
-
.card { &__title { } }✅ 编译为.card__title -
.card { & .card__subtitle { } }❌ 编译为.card .card__subtitle,带空格,破坏 BEM 封装性 - 所有
&后必须紧跟__或--,中间不能有空格或换行
团队协作中开始出现“谁动了这个类”的扯皮
当有人提 PR 改 .card 的 padding,却触发登录页按钮错位、商品列表截断,问题不在代码写错,而在选择器隐式依赖 DOM 层级。BEM 不是靠名字更长防冲突,而是把归属、层级、状态锁进类名,让冲突变成一眼可查的结构错误。
最容易被忽略的一点是:BEM 的 __ 和 -- 是工具链识别结构的硬锚点,不是风格偏好。漏写一个下划线、用单 _ 替代 --,会导致 stylelint-selector-bem-pattern 失效、VS Code 插件无法高亮、构建时样式提取逻辑出错——这些都不是运行时问题,而是在开发阶段就该拦截的契约断裂。


















