BEM 通过 block-element-modifier 三段式命名杜绝样式冲突,错误拼写导致选择器失效而非覆盖;中大型项目必须用 BEM 而非通用类名,否则必爆样式冲突。

直接用 .button 或 .title 这类通用名,中大型项目里样式冲突不是“会不会发生”,而是“什么时候炸”。BEM 不是靠名字变长来防冲突,而是把归属(block)、层级(element)、状态(modifier)全锁进类名字符串里——写错一个下划线,样式就根本找不到目标,而不是默默被覆盖。
为什么 .button 一定会覆盖 .user-card__avatar
浏览器不认“谁写的 CSS”,只按类名字符串完全匹配。只要两份样式都定义了 .button,后加载的规则就赢;而 .user-card__avatar 和 .profile-card__avatar 是两个完全不同的字符串,天然不匹配、不覆盖。
-
.button:hover在 A 模块改背景色 → B 模块所有按钮跟着变,因为没 block 上下文绑定 -
.user-card__avatar只响应user-card模块内定义的样式,哪怕profile-card也写了__avatar,它们互不感知 - 错误写法:
.card .title—— DOM 加个 wrapper 就失效;正确是.card__title,不依赖父级结构
__ 和 -- 到底怎么分,写错一个下划线会怎样
__ 表示严格子元素,-- 表示状态或变体。少一个字符,CSS 选择器就失效,DevTools 里根本搜不到对应样式规则。
-
search-form__input--disabled✅(search-form是 block,input是其 element,disabled是状态) -
search-form__input_error❌(单下划线非标准,应为--error) -
button--primary❌(缺少 block 前缀,button不是合法 block 名,应为checkout-button--primary) -
header__nav-item_active❌(nav-item是另一个组件,不该塞进header的命名空间)
JS 动态拼 className 最容易踩的三个坑
手拼字符串看着快,实际极易漏空格、错连字符、大小写混用,而且构建环境对大小写敏感度不同,本地跑得通,CI 上直接挂。
立即学习“前端免费学习笔记(深入)”;
- ❌ 危险写法:
className={`button button--${variant} ${hasIcon ? 'button__icon' : ''}`}(缺空格、无防错、button__icon脱离 block) - ✅ 推荐用
clsx或封装常量:const BLOCK = 'search-form'; const cn = (e, m) => `${BLOCK}${e ? '__' + e : ''}${m ? '--' + m : ''}` - Vue/React 中避免直接写
class="search-form__input"后再 JS 控制显示隐藏——要用search-form__input--hidden这类 modifier,保持结构一致
真正难的不是记住三段式,而是当 user-profile-card__header-section-title-text 这种超长名出现时,得意识到:不是 BEM 写错了,是 block 粒度太大、结构该拆了——这时候该问的不是“怎么命名”,而是“这个东西到底是不是一个独立功能单元”。


















