因为.btn是全局类名,浏览器不区分来源模块,后加载的.btn:hover必然覆盖前一个;bem通过search-form__submit:hover等带块前缀的类名实现天然隔离,避免跨模块干扰。

为什么 .btn:hover 会全局生效而不是只作用于当前按钮
因为 .btn 是全局类名,浏览器不区分它来自哪个模块。只要两个文件都定义了 .btn:hover,后加载的就会覆盖前一个——这不是“可能出问题”,是“必然覆盖”。BEM 把样式归属锁死在块级:比如 search-form__submit:hover 和 user-card__action-btn:hover 天然隔离,哪怕 DOM 结构一模一样也不会互相干扰。
常见错误现象:
- 改登录页的按钮悬停色,结果注册页、弹窗里的按钮全变了
- 用
.card .title写样式,后来加了个<div class="wrapper">,标题样式突然失效 <p>正确做法:</p> <ul> <li>所有元素必须带 block 前缀:<code>card__title,不是.title或.card-title - 禁止使用后代选择器:
.card .card__title❌,应写成.card__title✔️ - Modifier 必须和基础类共存:
class="button button--primary",不能只写button--primary
老项目里 class="user-name" 和 class="user__name" 共存怎么收口
硬推全量重命名风险高、成本大,真正可行的是“新老共存 + 边界收敛”。重点不是改完所有旧代码,而是守住新增入口。
实操建议:
在 Linux 上通过 Chrome 挂载/VNC 自动发布小红书长笔记。Agent 撰写标题和正文后执行 xhs_publish.sh --publish --submit,触发小红书发布。
- 对已有高频冲突类(如
.btn、.card),提取为 block:button-block,旧样式暂时用属性选择器兼容:[class^="button"]或[class*="button__"] - 给遗留结构包 wrapper:
<nav class="header-nav"></nav>,后续扩展统一用header-nav__item - CI 阶段用
stylelint-selector-bem-pattern拦截非 BEM 写法,比 Code Review 更早发现问题
React/Vue 里动态拼接类名时为什么总漏掉 block 前缀
手拼字符串极易出错:className={`card__body ${isCollapsed ? 'card__body--collapsed' : ''}` 看似简单,但漏写 card__body 基础类、大小写混用(Card__body)、连字符错写成单横线(card__body-disabled)都会导致样式失效或 SSR hydration mismatch。
推荐方案:
- 用
clsx或classnames封装条件逻辑:cn('card__body', { 'card__body--collapsed': isCollapsed }) - 封装 BEM 工厂函数:
bem('card', { collapsed: isCollapsed })返回数组,避免字符串拼接 - 禁止在模板里直接写
class="card__body card__body--${status}"—— 这种写法无法被 lint 工具识别,也容易漏掉基础类
第三方组件(如 Ant Design)输出的 class 不可控怎么办
BEM 不是银弹,强推改别人代码会卡住上线节奏。关键是在交界处设隔离层,而不是硬改源码。
典型做法:
- 用 wrapper block 包一层:
<div class="my-dashboard__ant-table-wrapper">,再通过 <code>.my-dashboard__ant-table-wrapper .ant-table局部覆盖 - 用 CSS Modules 的
:global()或composes显式声明边界,避免样式泄漏到外部 - 允许保留 utility class(如
u-text-center),但仅限纯功能性原子类,且不参与 Block 内部结构语义
真正难的不是写出 block__element--modifier,而是每次加一个新组件时,本能地新建 /components/tooltip/ 文件夹,而不是往 modal.css 里塞 .modal__tooltip —— 这需要目录约束、CI 检查,以及团队对“块边界”的持续共识。










