bem禁止使用div.card等标签选择器,因其将样式强耦合dom结构,破坏类名自身表达的作用域隔离,导致html结构调整时样式立即失效。

直接用 div、h2 这类标签选择器会立刻破坏样式隔离
BEM 的样式作用域完全靠类名自身表达,一旦混入 div.card 或 .user-card h3,就等于主动把样式绑死在 HTML 标签和层级结构上。真实项目里,h3 某天被改成 div 带 role="heading",或者 .user-card 被包进一个新容器,所有依赖标签或后代关系的样式当场失效。
常见错误现象:
-
.header nav ul li a改写成.header__nav ul li a—— 表面加了 BEM 前缀,实际仍依赖ul/li结构,换 Flexbox 就全崩 -
.card h2和.article h2在全局 CSS 中互相覆盖,查半天才发现没加命名空间
.card__title 比 .card h2 更可靠,不只是因为“能用”,而是因为“不怕动”
浏览器匹配选择器是从右往左的。.card h2 需要先找所有 h2,再逐个向上检查父级是否含 .card;而 .card__title 是单类名,匹配开销几乎为零。更重要的是语义稳定:
-
.card__title只认这个类名,挪位置、换标签、抽成独立组件,只要类名还在,样式就生效 -
.card h2依赖 DOM 层级和标签类型,任意一环变动,样式就断,且 DevTools 里看不出问题根源 - 微前端或多团队协作时,
.button这种泛名根本不敢用,button标签选择器更会跨模块误命中
伪类和交互状态不该塞进修饰符,但更不该靠标签选择器兜底
.card__title:hover 是合法且推荐的写法;.card__title--hover 是典型误用。BEM 的 -- 修饰符只描述静态、可复用的状态(如 --disabled、--compact),而 :hover 是用户触发的临时反馈,不属于组件固有状态。
如果硬要用标签选择器模拟交互,比如 h2:hover,问题会放大:
- 无法区分“卡片标题悬停”和“文章标题悬停”,样式逻辑混在一起
- JS 主动添加
--hover类时,会和真实:hover冲突,调试时分不清是哪段逻辑生效 - 当组件用
asChild或透传渲染(如 Radix UI)把h2替换成span,h2:hover直接失效
CSS-in-JS 或 CSS Modules 里仍得守 BEM 的边界意识
哪怕用了 styled.div 或 import styles from './Card.module.css',只要写出 div.card__title 或 .card__title h2,就等于放弃 BEM 的工程价值。工具能哈希类名、隔离作用域,但堵不住人写的耦合逻辑。
实操中容易踩的坑:
- SCSS 里写
.card { &__content { p { } } }→ 编译出.card__content p,又回到后代选择器陷阱 - React 中用
clsx拼card__title很安全,但若漏掉 Block 名,比如只写__title,重构时根本没法批量定位 - 第三方组件(如 Ant Design)内部节点不遵循 BEM,此时应在它外层包一层
.user-list__table-wrapper,测试和样式都只操作 wrapper,不深入内部结构
BEM 最难坚持的不是写法,而是拒绝用标签选择器、拒绝嵌套、拒绝在类名里塞样式值(比如 --bg-blue)。这些约束看着反直觉,但每一条都在堵一个真实发生过的线上样式事故入口。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











