bem是通过block、element、modifier三类命名约定实现样式作用域隔离的规范,避免依赖嵌套和优先级,从而减少样式冲突;其核心在于用类名本身表达结构关系与边界。

什么是BEM,为什么它能减少样式冲突
BEM(Block Element Modifier)不是语法糖,是命名约定。它强制把样式作用域拆成三类:block(独立功能模块)、element(模块内不可拆分的子部件)、modifier(状态或变体)。不靠嵌套层级,也不靠CSS优先级硬刚,而是靠名字本身表达关系和边界。
-
block 必须语义清晰、可复用,比如 header、card、input-field
-
element 用双下划线连接,如 card<strong>title</strong>、cardcontent,不能脱离 card 单独存在
-
modifier 用双连字符,如 card--wide、button--disabled,只改外观/行为,不改变结构
block 必须语义清晰、可复用,比如 header、card、input-field element 用双下划线连接,如 card<strong>title</strong>、cardcontent,不能脱离 card 单独存在 modifier 用双连字符,如 card--wide、button--disabled,只改外观/行为,不改变结构 常见错误:写成 card<strong>title--large</strong> —— modifier 应该挂在 block 上,不是 element 上;正确是 card--large cardtitle。
BEM命名在真实项目里怎么落地
落地难点不在规则,而在团队协作时的“松动”。比如有人图快写 .user-info__name,但另一处又出现 .user-name,两个看似相似的块实际无法复用,后期维护成本翻倍。
- HTML 中每个 class 必须严格对应 BEM 结构,避免混用非 BEM 类名做样式控制(如
text-center、mt-4 这类 utility class 只能用于布局微调,不能参与组件逻辑)
- CSS 文件按 block 拆分,一个
card.css 只包含 .card、.card__<em></em>、.card--,禁止跨 block 写 .card .button
- 使用预处理器时,禁用嵌套生成 BEM —— Sass 的
&__title 看似方便,但容易掩盖结构误用,建议手写全名更可控
text-center、mt-4 这类 utility class 只能用于布局微调,不能参与组件逻辑) card.css 只包含 .card、.card__<em></em>、.card--,禁止跨 block 写 .card .button &__title 看似方便,但容易掩盖结构误用,建议手写全名更可控 示例错误写法:
.card { &<strong>title { color: #333; } } → 正确应为.card</strong>title { color: #333; },否则调试时找不到真实类名。
遇到 legacy 代码或第三方组件怎么兼容BEM
BEM 不是银弹,强推会卡住上线节奏。关键是在交界处设隔离层,而不是硬改别人代码。
- 第三方组件(如
react-datepicker)输出的 class 不可控,用 wrapper block 包一层,比如 form-field--date,再通过属性选择器或 :global(如用 CSS Modules)局部覆盖
- 老项目中已有大量
.user-name 类,不要重命名为 .user__name 后直接替换——先并存一段时间,用 [class^="user"] 日志监控旧类使用位置,再逐步收敛
- 工具链里加 ESLint 插件
stylelint-selector-bem-pattern,配置 enforce strict pattern,CI 阶段拦截不合规写法,比 Code Review 更早发现问题
react-datepicker)输出的 class 不可控,用 wrapper block 包一层,比如 form-field--date,再通过属性选择器或 :global(如用 CSS Modules)局部覆盖 .user-name 类,不要重命名为 .user__name 后直接替换——先并存一段时间,用 [class^="user"] 日志监控旧类使用位置,再逐步收敛 stylelint-selector-bem-pattern,配置 enforce strict pattern,CI 阶段拦截不合规写法,比 Code Review 更早发现问题 性能影响几乎为零,但命名过长(如 filter-sidebar__item-button--active)可能增加 gzip 后体积,一般可接受;真有瓶颈时,构建阶段用 PostCSS 插件做类名压缩(需配套 source map 映射)。
为什么很多人用BEM还是踩坑
根本原因不是规则难,是混淆了「命名」和「职责」。BEM 解决不了组件粒度不合理、状态管理混乱、CSS-in-JS 和传统 CSS 混用等问题。
- 把
button 拆太细(button<strong>icon</strong>、buttonlabel、button__spinner)会导致 DOM 层级膨胀,不如让 button 自己封装内部结构,对外只暴露 button--loading
- 在 Vue/React 中用
scoped 或 CSS Modules,就别再套 BEM —— 两者目标重叠,叠加反而增加冗余
- 修改
modifier 时没同步更新 JS 判断逻辑,比如加了 input-field--error,但表单校验仍只操作 is-error class,样式和行为脱节
button 拆太细(button<strong>icon</strong>、buttonlabel、button__spinner)会导致 DOM 层级膨胀,不如让 button 自己封装内部结构,对外只暴露 button--loading scoped 或 CSS Modules,就别再套 BEM —— 两者目标重叠,叠加反而增加冗余 modifier 时没同步更新 JS 判断逻辑,比如加了 input-field--error,但表单校验仍只操作 is-error class,样式和行为脱节 最常被忽略的一点:BEM 的 block 必须能独立存在、可截图、可写 Storybook 演示。如果一个所谓 block 总要依赖父容器样式才能正常显示,那它大概率不是 block,只是 fragment。
立即学习“前端免费学习笔记(深入)”;










