bem 的核心是让类名自带上下文,如 header__logo--dark 明确标识所属模块、元素及状态,提升可读性与协作效率,而非追求形式专业。

直接说结论:BEM 不是让类名“看起来更专业”,而是让每个类名自带上下文——看到 header__logo--dark,不用翻 HTML 或 JS 就能确定它属于哪个模块、是什么元素、处于什么状态。这是可读性的本质,不是装饰。
为什么 header__logo--dark 比 logo-dark 更可靠
类名里没写 header,你就永远不确定这个 logo-dark 是全局暗色 logo,还是仅用于页头的变体。而 header__logo--dark 明确声明:它属于 header 块、是其 logo 子元素、当前启用 dark 修饰态。
- 常见错误:
.user-card-avatar或.avatar-user-card——分隔符缺失或顺序混乱,无法被stylelint-selector-bem-pattern校验,协作时靠猜 - 模块名必须是独立功能单元:
card✅,section❌(太泛);user-profile✅(含短横可接受),但不能写成user-profile__avatar--large❌(--large应作用于user-profile或user-profile__avatar,不能跨层) - 性能影响极小,但语义错位会持续拖慢排查速度:审查元素看到
.btn-primary,你得点开 HTML 确认它是不是在.modal里被意外覆盖;而.button--primary直接告诉你——这是按钮组件自身的主色调变体
哪些 __element 该升格为独立 block
子元素用双下划线 __ 连接,比如 .card__title、.card__footer。但一旦某个子元素具备复用性、独立样式逻辑或可能脱离当前块存在,它就该升格为独立 block。
- 使用场景:
.product-item__price-tag→ 如果.price-tag同时出现在商品列表、购物车弹窗、订单详情页,且样式/行为一致,那就该拆成独立.price-tag块 - 判断依据:这个类是否会在其他 DOM 结构中出现?是否需要单独测试、文档化、或被其他团队引用?
- 嵌套过深即危险信号:
.card__content__header__title❌——BEM 只允许一层__,多级意味着结构失控;应改为.card__title或另起.title块
Modifier 写错位置,比写错名字更致命
BEM 中修饰符(--)必须绑定到明确的 block 或 element 上,不能脱离主体存在。错误写法如 btn--primary 单独出现却没定义 btn,或把状态类写成 is-active 这类无上下文的通用名。
- 正确做法:
修饰 block:menu--vertical(整个菜单切换为垂直布局)
修饰 element:menu__item--current(仅当前菜单项高亮) - 避免纯布尔名:
button--loading比button--is-loading更简洁,也比loading更安全 - 不复合 modifier:
button--primary--small不推荐,应合并为button--primary-small或拆成两个独立 modifier(button--primary+button--small),否则 JS 控制时容易漏删 - JS 里操作类名必须严格匹配:
el.classList.add('card__body--expanded'),别用字符串拼接'card__body' + '--' + state(易出错)
最常被忽略的点不是命名规则本身,而是 Block 名和文件名的对齐——UserCard.vue 对应的 Block 名只能是 user-card,不是 card 或 user-card-component。一旦松动,整个 BEM 的上下文链就断了,类名再长也只剩字符,没有意义。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











