直接写 .header 会导致样式冲突,因其泛用性在多人协作中易被重复定义,CSS 全局作用域下后加载样式覆盖先加载的,且排查困难;BEM 通过 block__element--modifier 结构强制明确归属与状态,提升可维护性与删除确定性。

为什么直接写 .header 会导致样式冲突
大型网站多人协作时,.header 这种泛用类名几乎必然被重复定义。不同模块的 .header 可能分别控制导航栏、弹窗标题、表格头部,但 CSS 是全局作用域——后加载的样式会覆盖先加载的,且开发者很难立刻意识到自己改坏了别人的功能。
常见错误现象:margin 突然变大、字体颜色错乱、hover 效果在非预期区域触发;排查时发现 DevTools 里多个 .header 规则叠加,权重相同,靠加载顺序决定生效结果。
关键不是“能不能用”,而是“谁在用、在哪用、会不会被误伤”。BEM 不是加前缀这么简单,它强制你回答这三个问题。
__ 和 -- 的分工不能颠倒
BEM 的三段式结构 block__element--modifier 中,__ 表示“属于谁”,-- 表示“现在是什么状态”。颠倒就会破坏语义层级,让后续维护者无法快速判断一个类名是独立组件还是某块的子部分。
立即学习“前端免费学习笔记(深入)”;
使用场景举例:
- 正确:
user-card__avatar(头像属于用户卡片)、user-card--compact(整张卡片进入紧凑模式) - 错误:
user-card--avatar(暗示“avatar 是一种修饰态”,但 avatar 是实体元素) - 错误:
user-card__compact(compact 不是结构部件,是状态切换)
参数差异直接影响可维护性:一旦把修饰符写成元素,后续想加 user-card--dark 就会和 user-card__compact 并列,逻辑割裂;而统一用 --,所有状态类可批量控制、条件注入、甚至运行时切换。
如何让 BEM 真正起效,而不是变成命名负担
光靠人肉遵守规则撑不过两个迭代。必须嵌入开发流程,否则很快退化为“看着像 BEM,实际还是 .btn-red 风格”。
实操建议:
- 用 ESLint 插件
stylelint-selector-bem-pattern在保存时校验类名格式,不合规直接报错 - 组件模板里禁用自由
class绑定,只允许通过blockName+modifiers对象生成类名(如 Vue 的:class="bem('button', { loading: true })") - 禁止在 SCSS 中用
&__item嵌套生成元素——看似省事,实则掩盖了真实 DOM 结构,导致 JS 查询.menu__item时可能匹配到非当前 block 的同名元素
性能影响很小,但兼容性上要注意:过长类名(如 product-list__item__image--rounded--shadow)在老版 IE 中可能触发选择器长度限制,实际项目中建议单个类名不超过 40 字符,复杂状态优先走 CSS 自定义属性或 JS 控制类切换。
遇到第三方 UI 库时怎么处理 BEM 混合
React 的 Ant Design 或 Vue 的 Element Plus 默认不遵循 BEM,它们的类名如 el-button、ant-modal-header 是自成体系的。硬套 BEM 会增加冗余,放任不管又可能污染全局。
推荐做法:
- 对第三方组件只封装一层 wrapper 元素,用 BEM 类名包裹,例如
<div class="my-form__submit-area"><el-button>提交</el-button></div>,样式作用域限定在 wrapper 内 - 避免用
!important覆盖第三方样式,改用更高权重的选择器,比如.my-form__submit-area .el-button而非.el-button - 如果必须深度定制(如改
el-input内部 placeholder 颜色),把覆盖样式写在该 wrapper 的 SCSS 文件里,并加注释说明“此处侵入第三方 DOM 结构,升级库版本需回归验证”
最容易被忽略的是:BEM 的价值不在“写得漂亮”,而在“删得安心”。当你看到 search-bar__input--disabled,就能确定删掉它只影响搜索栏输入框的禁用态,不会牵扯到用户头像或分页按钮——这种确定性,在万行 CSS 的项目里比任何炫技都重要。


















