bem 不减少代码行数,但通过将隐式依赖显式写入类名(如.user-card__title),避免因dom结构变动、全局样式覆盖或类名冲突导致的意外样式失效和模块间干扰。

直接说结论:BEM 本身不减少代码行数,但能把「改一个 .title,结果购物车弹窗全崩」这类问题从概率事件变成可规避的确定性风险——关键在于把原本藏在 DOM 结构、祖先类、标签类型里的隐式依赖,全部显式写进类名里。
为什么改 .title 会意外影响购物车和弹窗
传统写法如 .user-card .title 或 .header h3 把样式生效条件绑死在 HTML 层级上。DOM 一动,样式就静默失效;更危险的是,它可能意外匹配到另一个模块的 .title,导致改 A 却崩 B。
- 把
<h3></h3>换成<div role="heading"> → 样式立刻消失,控制台无报错 <li>侧边栏也用了 <code>.title→ 购物车标题字体突然变小,排查时才发现是全局 CSS 覆盖 - 抽离
product-card组件到新页面 → 所有样式失效,因为外层容器没了.product-card类 - 使用 BEM 后,每个组件自带命名空间:
.auth-form__submit只属于登录表单,不会影响.settings-form__submit -
.card__title和.modal__title完全隔离,哪怕样式规则一模一样 - 修饰符如
--disabled或--loading描述状态,不是尺寸或颜色,避免后续响应式或主题切换时要全局搜200px改成100vw - 漏写 Block 前缀:
className={isFeatured ? 'featured' : ''}→ 应为product-card--featured - 连字符写错:
card__title--large(非法)→ 正确是card__title--large(注意双中划线只用于 modifier) - 大小写混用:
Card__header和card__header在 Linux 构建环境视为不同类名,样式丢失且无报错 - JS 里硬编码的选择器:
document.querySelector('.user-card .avatar')→ 改成 BEM 后必须同步更新为.user-card__avatar - 预处理器里滥用
@extend或@mixin,绕开 BEM 边界,比如 Sass 中@extend .button导致非 button 元素意外获得按钮样式 - CSS 加载顺序引发的覆盖:如果
.card__title--large在 CSS 文件里写在.card__title--small后面,后者就会被覆盖——哪怕它在 HTML 里写得更靠前
BEM 强制写成 .user-card__title 或 .header__logo,类名自带作用域——它不关心你在哪、嵌多深、用什么标签,只认这个字符串。
.btn 和 .button 在协作中为何天然引发冲突
多人并行开发时,泛义类名缺乏上下文。.btn 可能被登录页、弹窗、表单各自复用,但没人能保证它在所有地方都该有 margin-right: 8px。一旦某人加了 .btn--primary,另一个人却在 JS 里写 el.classList.add('btn'),结果就是按钮在某个模块突然变宽——而审查元素只看到两个 .btn,根本分不清哪个生效。
JS 动态拼接类名时最容易漏掉的三个断裂点
React/Vue 中不是逻辑写错才出问题,而是拼写和结构断裂:
建议统一用 clsx 或封装 bem('card', { featured: isFeatured }) 工厂函数,别手写模板字符串。
重构时最常被忽略的「看不见的耦合」
真正卡住进度的,往往不是技术方案,而是三类隐性依赖:
这些点不报错、不警告、不崩溃,但会让重构变得不可预测。BEM 能防住其中两类,第三类必须靠构建时确定顺序(如 Webpack 的 mini-css-extract-plugin 配置)+ 人工盯住。











