bem的核心是用命名契约锁定样式所有权,而非简单加__和--;必须严格遵循块__元素--修饰符规则,禁止混搭、孤立修饰符或结构依赖,否则导致样式失效或维护失控。

直接说结论:BEM不是靠“加__和--”就自动可维护,而是用命名契约把样式所有权钉死在组件上;不按规则写,button__icon可能被全局.icon覆盖,card--dark可能根本没生效——浏览器不报错,但样式就悄悄失效了。
为什么.card .title必须改成.card__title
后代选择器会把样式和DOM结构强绑定。一旦卡片被嵌套进.modal或.sidebar,.card .title可能意外匹配到外部节点;更隐蔽的是,别人改了全局.title的margin,你的卡片顶部间距就崩了。
-
.card__title是独立类名,不依赖父容器是否存在、是否在某一层DOM中 - 禁止写
.card > .header或.card .card__title——前者断链风险高,后者冗余且破坏扁平语义 - 所有子元素必须显式声明归属:
.card__title、.card__subtitle、.card__footer-action,不能靠结构推导
button__icon--primary为什么是错的?
双下划线__只表归属,双短横--只表状态,二者不能混搭层级。button__icon--primary会被解析为“一个叫icon--primary的元素”,而你要表达的是“按钮处于primary状态时,其图标颜色随之变化”——这属于块级状态,不是图标自身的变体。
PigX UI Pro 前端开发指南 - Vue 3 + TypeScript + Element Plus。当用户提到 PigX UI、PigX 前端、lgb-mgui 项目、Vue 3 企业级后台开发、Element Plus 后台开发时使用此技能。
- 正确写法:
button--primary(按钮整体状态),再由CSS变量或内部规则控制button__icon颜色 - 若图标真有独立状态(如加载中旋转),才用
button__icon--loading - 绝对禁止
button--primary__icon:修饰符不能挂在元素的修饰符上,BEM工具会直接忽略 - 常见翻车点:
dialog__header__title❌ 应为dialog__title✅
组件目录结构怎么和BEM块名对齐?
目录名即块名,全小写、中划线分隔,且一一对应。这不是为了整齐,而是让grep能立刻定位、CI能自动拦截非法引用。
- 按钮组件必须放在
/components/button/,只允许button.css、button.tsx等归属该块的文件 - 禁止把
button__icon抽成/components/icon/——它不是独立块,强行拆分会破坏语义闭环 - 禁止在
button/目录里塞card.css,CI检查会立刻fail - 如果按钮里用了头像,就直接引用
/components/avatar/,而不是在button/下建avatar/
修饰符能不能单独写class="button--primary"?
不能。修饰符没有上下文就是语义断裂——没人知道这个--primary是按钮、输入框还是弹窗的。它必须依附于宿主存在,否则复用时极易误判。
- 合法:
class="button button--primary"、class="button button__icon button--primary" - 非法:
class="button--primary"、class="disabled"(孤立修饰符) - 性能无影响,但维护成本飙升:比如
input--error单独出现,你得翻三遍代码才能确认它绑定的是哪个表单控件 - React里别手拼:
className={`button button--${variant}`}容易漏空格;推荐封装bem('button')({ primary: true })保证源头统一
BEM最难的部分不在写法,而在克制——忍住不把__当嵌套用,忍住不把--当万能开关,忍住不在HTML里堆一堆“看起来有用”的类名。一旦松动,语义就断链,后面所有人查bug都要多绕两圈。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!










