bem本身不生成选择器,只定义类名结构;真正可维护的选择器是仅用单个类名、不嵌套、不依赖html层级的规则。.card .card__title违背bem初衷,因其重陷dom结构依赖,破坏作用域隔离,增加权重与性能开销,且被工具链视为非法。

直接说结论:BEM本身不生成选择器,它只定义类名结构;真正可维护的选择器,是那些**只用单个类名、不嵌套、不依赖HTML层级**的规则。写错类名或滥用SCSS嵌套,反而会让BEM变成维护负担。
为什么.card .card__title这种写法违背BEM初衷
BEM要解决的核心问题是样式作用域失控,而.card .card__title又把选择器拉回“依赖DOM结构”的老路——一旦card__title被挪到另一个容器里,样式就失效;更糟的是,它权重比.card__title高,容易覆盖其他地方同名类。
- 浏览器匹配是从右往左,
.card__title能直接命中,.card .card__title得多查一层父节点,性能略差 - 团队协作时,有人改了HTML结构但没同步CSS,bug难定位
- 工具链(如
postcss-bem-linter)会报.card .card__title为非法,因为它不是扁平类名
SCSS里&__content到底能不能用
能,但必须严格限定在块级顶层。很多人以为&__content是语法糖,其实它只是字符串拼接,拼错位置就全乱。
- ✅ 正确:
.card { &__header { } &--expanded { } }→ 编译出.card__header和.card--expanded - ❌ 错误:
.card { .header { &__title { } } }→ 编译出.card .header__title,这不是BEM元素 - ❌ 更隐蔽的错误:
.button { &:hover { &__icon { } } }→ 编译出.button:hover__icon,浏览器直接忽略这个非法类名
Modifier该挂在哪一级:block还是element
绝大多数情况下挂block上。Element加Modifier(如card__title--large)看似合理,实则暴露设计缺陷。
- 状态应属于组件整体,而非某个子部分:按钮禁用是
button--disabled,不是button__text--disabled -
card__title--large意味着标题尺寸由内容决定,但实际应由卡片变体控制(card--compact或card--spacious) - 如果真需要差异化控制某个元素的状态(比如只让图标旋转),优先用独立类名
icon--spinning,而不是依附于父块
旧项目接入BEM时最容易踩的命名坑
不是类名太长,而是语义漂移——看着像BEM,实际失去作用域隔离能力。
- Block名塞业务上下文:
homepage-hero-banner→ 应拆成hero-banner+hero-banner--homepage - Element名写行为而非角色:
user-card<strong>delete-btn</strong>→ 改为user-card<strong>control</strong>+user-cardcontrol--delete - Modifier用视觉描述:
input--big→ 没有基准,“big”对谁而言?换成input--expanded或input--compact
最麻烦的不是写错一个类名,而是让user-card和user-profile-card并存——它们看起来像同一类组件,但样式逻辑完全不共享,后期合并成本极高。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











