BEM 通过显式命名和构建校验解决样式冲突:类名自带模块上下文(如.search-form__submit),配合 stylelint-selector-bem-pattern 和 postcss-bem-linter 强制 block 白名单与结构规范,杜绝泛义名(如.btn)覆盖问题,并通过 @scope 注释、函数生成 className、CSS Modules 保持命名一致性。

因为 BEM 把“谁改了哪个样式”从隐式依赖变成显式字符串,冲突不再随机发生,而是直接暴露为命名违规——只要工具链守住 block__element--modifier 结构和白名单,两个人同时提交 .search-form__input 和 .user-form__input 就根本不会撞。
为什么 .btn 一合并就出事,而 .search-form__submit 不会
浏览器不认“这是登录页的按钮”,只认选择器权重和加载顺序。.btn 这种泛义名在多个文件里重复定义,后加载的 CSS 直接覆盖前一个,没人能提前预判哪条生效。BEM 类名自带上下文:.search-form__submit 天然绑定到搜索表单模块,哪怕另一个模块也写了同名规则,只要它放在不同 CSS 文件、且构建时没混进同一作用域,就不会互相干扰。
- 多人并行开发时,
.btn可能被 A 组用在弹窗、B 组用在表单、C 组用在侧边栏,最终打包成一份 CSS 后,三处按钮样式全乱 -
.search-form__submit只会在search-form/目录下出现,CI 扫描到其他地方用了这个类名,立刻报错 - 修饰符如
--loading描述状态,不是尺寸或颜色,避免后续主题切换时全局搜20px改成1.25rem
构建层才是 BEM 起效的关键,不是人写得对不对
靠人自觉写对 block__element--modifier 没用。真正起作用的是 stylelint-selector-bem-pattern + postcss-bem-linter 的组合:前者禁止出现 .input 这类裸名,后者校验 block 名是否在预设白名单(如 ['button', 'card', 'search-form'])里。
- 没配 linter 时,有人随手写
.my-button__icon,既绕过命名约束,又污染全局空间 - 白名单强制 block 名唯一,
search-form和searchBox不能共存,避免语义近似但拼写不同的类名撞车 - 所有 CSS 文件开头加
/* @scope search-form */注释,CI 可扫描归属,防止某人把header.css里的规则偷偷复用到用户页
JS 拼 className 是最隐蔽的冲突源
手拼字符串看着快,实际是定时炸弹:className={`button button--${variant} ${hasIcon ? 'button__icon' : ''}` 缺空格、漏连字符、button__icon 脱离 block 上下文,CI 里一跑就挂。
立即学习“前端免费学习笔记(深入)”;
- 正确做法是用函数生成:
cn('search-form', { 'search-form--compact': isCompact }),确保结构合法 - 禁止在 JS 里直接写
el.classList.add('is-loading'),必须用search-form__submit--loading这种完整 BEM 名 - React/Vue 中若用 CSS Modules,导出变量仍应保持 BEM 命名:
styles['search-form__input'],别混用inputClass这种模糊名
真正容易被忽略的不是怎么写类名,而是没人检查“谁有权改 search-form 这个 block”。一旦目录结构、文件注释、构建校验三者脱节,BEM 就退化成带下划线的普通命名。


















