bem通过block__element--modifier三段式将归属、层级、状态锁进类名,使样式冲突从随机覆盖变为结构错误可查;.button因全局作用域易被覆盖,而.user-card__avatar等带明确block前缀的类名天然隔离、互不干扰。

直接用 .button 或 .header 这类通用名,必然在中大型项目里出样式冲突——BEM 不是“让名字变长”,而是用 block__element--modifier 把归属、层级、状态全锁死在类名里,让冲突从“随机覆盖”变成“结构错误,一眼可查”。
为什么 .button 会覆盖 .user-card__avatar
浏览器不认“谁写的”,只按层叠顺序和优先级匹配类名。只要两个文件里都定义了 .button,后加载的就会生效;而 .user-card__avatar 和 .profile-card__avatar 天然不同名,哪怕共用同一份 CSS 文件也不会互相干扰。
-
.button:hover在 A 模块改背景色,B 模块的按钮也会变——因为没上下文绑定 -
.user-card__avatar只属于user-card这个功能单元,哪怕页面里有十个用户卡片,每个头像样式都独立可控 - 错误写法:
.card .title—— DOM 结构加个 wrapper 就失效;正确是.card__title,不依赖父级
怎么写才不算 BEM,但看起来像 BEM
常见伪 BEM:类名带双下划线却没 block 上下文,或 modifier 脱离 block 单独使用。这类写法失去隔离能力,调试时根本看不出样式来源。
-
search-form__input--disabled✅(search-form是 block,input是其元素,disabled是状态) -
search-form__input_error❌(单下划线非标准,应为--error) -
button--primary❌(缺少 block 前缀,button不是合法 block 名,应为myapp-button--primary或更具体如checkout-button--primary) -
header__nav-item_active❌(nav-item是另一个组件,不该塞进header的命名空间)
JS 里拼 className 容易踩哪些坑
手拼字符串看着快,实际极易漏空格、错连字符、大小写混用,而且构建环境对大小写敏感度不同,本地跑得通,CI 上直接挂。
- ❌ 危险写法:
className={`button button--${variant} ${hasIcon ? 'button__icon' : ''}`}(缺空格、无防错、button__icon脱离 block) - ✅ 推荐用
classnames或封装常量: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,保持结构一致 - SCSS 里禁止嵌套出后代选择器:
.search-form { &__input { &__icon {} } }是错的,&__input__icon不合法,应拆成独立 block
第三方组件(如 Ant Design)能套 BEM 吗
不能。强行给 .ant-btn 加 --primary 或写 .ant-btn__text,只会让选择器链断裂、动态类名失配、升级后全崩。
- 正确做法:用 wrapper block 包一层,比如
<div class="form-field--date"><datepicker></datepicker></div> - CSS Modules 下必须显式穿透:
::global(.ant-btn) { margin-right: 8px; },否则styles['ant-btn']是undefined - 注入顺序很关键:Ant Design 的 CSS 若在入口
import,可能比你的form-field--date规则优先级高,得靠 specificity 或调整<link>顺序压住
BEM 真正难的不是记规则,而是每次写 class 前多问一句:“这个样式属于谁?它会不会在别的上下文里意外生效?”——这句问题比任何命名规范都管用。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











