bem是一种css命名方法论,通过block-element-modifier三段式命名(如.menu、.menu__item、.menu--horizontal)解决样式作用域冲突问题,避免同名类名相互影响。

什么是BEM,为什么直接写 .header-nav 不行
BEM强制用三段式命名:Block(模块)、Element(元素)、Modifier(状态),比如 .menu、.menu__item、.menu--horizontal。不是为了好看,是为了解决CSS作用域失控——你改了 .nav,结果首页轮播图的“导航”也塌了,因为两个 .nav 碰巧同名又没隔离。
常见错误是把语义当结构,比如写 .user-card__name,但实际这个 name 在用户列表页也复用,它本质是独立 Block;或者把 Modifier 当成样式开关,写成 .button--red,结果设计师说“红色只是品牌色,换主题就得全搜替换”,其实该用 .button--primary 这种语义化修饰符。
实操建议:
立即学习“前端免费学习笔记(深入)”;
- Block 必须有独立功能和复用价值,不能只是视觉分组(比如
.section-2就不合格) - Element 名称不带父级语义,
.card__title正确,.card__card-title是冗余 - Modifier 值必须可预测,避免
--v2、--new这类临时标签
嵌套层级怎么控制,scss 里能写多深
BEM本身禁止CSS嵌套超过一层,即只允许 .block__element 和 .block--modifier,不允许 .block__element__subitem 或 .block__element .subitem。这不是教条,是因为每多一层选择器,就多一分耦合风险:你改 .list 的 display,可能意外影响 .list__item__icon 的定位逻辑。
SCSS 里用 &__ 和 &-- 是安全的,但一旦出现 &__item &__icon(中间带空格),就等于生成了后代选择器,破坏BEM隔离性。
实操建议:
立即学习“前端免费学习笔记(深入)”;
- SCSS 中只用单层嵌套:
.block { &__element {} &--modifier {} } - 遇到“子元素的子元素”,优先拆成新 Block,比如
.table里的.table-cell__badge应改为.badge(独立 Block)+.badge--in-table - 如果真需两层视觉关系,用属性选择器替代,比如
[data-badge-type="status"],比.cell__content__badge更可控
React 组件里怎么用 BEM,classnames 怎么配
React 中最常踩的坑是把 BEM 当字符串拼接:写 className={`card__header ${isExpanded ? 'card__header--expanded' : ''}`。问题不在语法,而在于 Modifier 状态分散在 JSX 多处,后续加个 disabled 状态就得再补一串三元。
classnames 库能收敛逻辑,但要注意它不解决 Block 命名一致性问题——你传入 card__header,组件内部却用 header 作 Block 名,最终生成的类名对不上。
实操建议:
立即学习“前端免费学习笔记(深入)”;
- 每个组件顶部定义 Block 名,如
const BLOCK = 'user-profile',所有类名基于它派生 - Modifier 用对象写法:
classnames(`${BLOCK}__avatar`, { [`${BLOCK}__avatar--large`]: isLarge }) - 避免在子组件里硬编码父级 Block 名,用 props 透传 Block 前缀,或用
useBem自定义 Hook 统一管理
为什么 .btn--primary 比 .btn-primary 更可靠
破折号(--)是 BEM 的语法锚点,它让类名具备机器可解析性。工具链能靠它区分 Block、Element、Modifier:PostCSS 插件可以自动校验 .menu__item--disabled 是否存在对应 .menu__item;VS Code 插件能高亮跳转到定义处;甚至 CI 脚本可以扫描出所有孤立的 Modifier 类名(写了 --hover 却没定义 __trigger)。
下划线风格(.btn_primary)或连字符风格(.btn-primary)会模糊边界:.btn-primary 看起来像 Block,但其实是 Modifier;.form-input-error 分不清是 form__input-error 还是 form-input__error。
实操建议:
立即学习“前端免费学习笔记(深入)”;
- 全项目统一用双破折号
--表示 Modifier,双下划线__表示 Element,这是 BEM 解析器唯一认的标准 - 别为了“更短”改成单破折号,
.btn-primary在 Webpack + CSS Modules 下容易和本地作用域冲突 - 如果团队已用 Tailwind,BEM 可退守为 Block 层命名规范(只约束
.card、.modal这些顶层类),Element/Modifier 交给 utility classes 承担
__ 前问一句:它未来会被抽出来单独复用吗?没人问,就容易变成又一套“看起来规范”的技术债。










