BEM通过block__element--modifier结构强制样式归属明确,使.class冲突从“随机覆盖”变为“结构错误可查”;.button必然覆盖同名类,而user-card__avatar等带block前缀的类名天然隔离、互不干扰。

直接用 .button 或 .header 这类泛化类名,在中大型项目里不是“可能冲突”,而是“必然炸锅”——BEM 不是加长名字的仪式,它靠 block__element--modifier 结构把样式归属锁死在类名里,让冲突从“DevTools 里划掉一堆线却找不到源头”变成“一眼看出 user-card__avatar 和 profile-card__avatar 根本不归一路”。
为什么 .button 一定会覆盖 .user-card__avatar
浏览器不认“谁写的”,只按选择器匹配+层叠顺序生效。只要两份 CSS 都定义了 .button,后加载的就赢;而 user-card__avatar 和 profile-card__avatar 天然不同名,哪怕打进同一份 app.css 也不会互相干扰。
-
.button:hover在 A 模块改背景色 → B 模块所有按钮跟着变,因为没上下文绑定 -
.user-card__avatar只属于user-card这个功能单元,页面里十个用户卡片,每个头像样式都独立可控 - 错误写法:
.card .title—— DOM 加个 wrapper 就失效;正确是.card__title,不依赖父级结构
怎么写才算真 BEM,而不是“看起来像”
伪 BEM 最常出现在类名带双下划线却没 block 上下文,或 modifier 脱离 block 单独使用——这类写法失去隔离能力,调试时根本看不出样式来源。
-
search-form__input--disabled✅(search-form是 block,input是其元素,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,保持结构一致
BEM 真正难的不是记规则,而是每次写前多问一句
“这个样式属于谁?它会不会在别的上下文里意外生效?”——这句话比任何命名规范都管用。更关键的是:BEM 本身只是命名约定,编译后类名仍是全局可见的;如果没配 CSS Modules 或没启用哈希类名,再规范的 BEM 也只是语义更清楚的全局污染。


















