bem通过块级命名空间隔离样式作用域,如header__logo--large天然绑定上下文,避免与nav__logo等冲突;强制语义化命名、限制嵌套层级、规范修饰符用途,并需团队对块划分达成共识。

为什么BEM能减少CSS选择器冲突
因为BEM强制把样式作用域锁死在块(Block)内部,header__logo--large 这种命名天然带上下文,不会和 nav__logo 或全局 logo 互相干扰。传统写法里 .logo 一加就炸,尤其多人协作时,谁也不知道哪个组件悄悄覆盖了它。
实操建议:
- 块名必须是功能语义化名词,比如
search-form,别用blue-box这种视觉描述 - 元素名(
__后)只能是块的直接子节点,search-form__input合法,search-form__input__icon不合法——那是嵌套块,该拆成独立icon块 - 修饰符(
--后)只表达状态或变体,如--disabled、--theme-dark,不写--margin-left-20这种纯样式值
怎么写BEM才不被同事骂“太啰嗦”
啰嗦感通常来自重复写块名。比如一个 card 块里有标题、内容、按钮,硬写成 card__title、card__content、card__button 看着冗长——但这是对的。省略块名(比如只写 __title)会导致脱离上下文后无法识别归属,Sass 的 &__title 可以压缩书写,但编译后仍是完整类名。
常见错误现象:
- 用
card-title替代card__title:丢失 BEM 层级标识,未来加user-card__title就没法区分 - 在 CSS 里写
.card .title:违背 BEM “无层级依赖”原则,DOM 结构一动样式就断 - 把修饰符当开关用,比如同时加
card--hover和card--active却没定义组合逻辑,结果样式打架
Sass里用BEM怎么避免嵌套过深
Sass 的 @nest 或 & 很方便,但容易写出 .block { &__elem { &--mod { ... } } } 这种三层嵌套——编译后类名没错,但维护时要数缩进,改起来心累。
实操建议:
- 单个块的样式写在一个文件里,文件名即块名:
_card.scss - 只嵌套一层:用
&__elem和&--mod,绝不出现&__elem--mod(元素修饰符应写成card__elem--mod) - 修饰符单独成行,和元素平级,别缩进在元素里面
- 需要组合状态时,用多个类名控制:
<div class="card card--featured card__button card__button--primary">,而不是靠嵌套推导 <h3>BEM和CSS-in-JS或Tailwind混用时要注意什么</h3> <p>混用不是不行,但边界必须清晰。比如用 Tailwind 写布局和原子样式,BEM 写组件专属逻辑样式——这时 BEM 类名只负责「这个组件长什么样」,不负责「它在哪」「多宽多高」。</p> <p>容易踩的坑:</p> <ul> <li>在 React 组件里用 <code>className={clsx('card', isHovered && 'card--hover')}没问题,但别在card--hover里写transform: translateY(-2px)这种和交互强耦合的样式,应该交给 JS 控制内联样式或动态 class - 用 CSS Modules 时,BEM 类名仍要完整写,别依赖
styles.card__title自动转成哈希——可读性和调试成本会飙升 - Webpack 的
css-loader开启modules.localIdentName后,BEM 名字会被 hash 掉,失去语义性;真要用模块化,优先选getLocalIdent自定义函数保留 BEM 原名
真正难的不是写对 BEM,是团队所有人对「什么算一个块」「什么时候该拆新块」有共识。没共识时,再规范的命名也救不了样式失控。











