BEM 命名规范通过 .block__element--modifier 三段式解决样式冲突与语义不清问题:__ 表示严格子元素,-- 表示状态/变体,禁用纯功能类名和动态值,兼容 scoped CSS 与 SSR,提升协作可维护性。

为什么直接用 .button 会炸锅
因为 CSS 是全局作用域,.button 在 header、modal、form 里各写一遍,谁的规则生效取决于加载顺序和选择器权重,不是你写的早就有优先权。更糟的是,团队协作时没人敢删旧类名——怕不小心干掉别人页面里的按钮样式。
常见错误现象:button 在 A 页面圆角正常,在 B 页面突然变直角;改了 .card 的 padding,结果侧边栏菜单也跟着撑开。
- 所有组件级样式必须带明确上下文前缀,比如
.header-logo、.product-card-title - 避免纯功能类名(如
.red、.mt-16),它们无法表达语义,也拦不住别人复用时误用 - BEM 不是强制要求双下划线,但
__和--是约定,别写成.card__title--large却漏掉一个_,CSS 选择器就失效了
__ 和 -- 到底怎么分
BEM 的三段式不是炫技,是解决“谁属于谁”和“谁在什么状态”的问题。.menu__item--active 表示:这是 .menu 的子元素 item,且当前处于 active 状态。少一段,语义就断档。
使用场景:写一个带图标和徽标的按钮,结构是 。这时 .btn__icon 能精准控制图标尺寸和对齐,不会影响其他地方的 .icon。
-
__(双下划线)只用于子元素,且必须是直接后代,不推荐嵌套三层以上(如.card__body__title) -
--(双短横)只用于修饰状态或变体,比如.btn--primary、.input--disabled,不能用来描述位置(如--top)或无关样式(如--flex) - 如果需要响应式变体,用
@media包住整块 BEM 规则,别单独写.btn--mobile—— 那等于又造了一个新命名空间
和 CSS-in-JS 或 Scoped CSS 冲不冲突
不冲突,但目标不同。BEM 解决的是「人类协作时类名不打架」,而 :scope 或 css-modules 解决的是「构建时自动加哈希防止运行时冲突」。如果你用 Vue 的 ,BEM 依然有用——它让类名可读、可搜、可维护,而不是一堆 data-v-1a2b3c4d。
性能影响几乎为零:BEM 类名略长,但 gzip 后差异可忽略;兼容性上,它只是字符串命名,IE8 都没问题。
- 用 Webpack + css-loader 时,
modules: true会把.btn__label编译成.btn__label_1x2y3z,BEM 结构还在,调试时照样能看懂 - 不要在 BEM 名称里塞动态值,比如
.user-card--size-{{ size }},这会让 CSS 文件膨胀,也违背 BEM 的静态语义原则 - 第三方 UI 库(如 Element Plus)自带 BEM 类名,你可以直接复用它的
.el-button__inner做微调,但别覆盖它的基础.el-button,否则升级库时容易崩
容易被忽略的边界情况
BEM 不处理「跨组件共享样式」。比如多个卡片都需要统一的阴影效果,你不会每个都写 .card__content--shadow,而是抽一个 .shadow-sm 工具类——但这个类必须是纯视觉修饰、无布局影响、且不依赖上下文。
另一个盲区是伪类和 JS 状态绑定:.btn--loading 是合理变体,但 .btn:hover 不需要额外类名,直接写在 CSS 里就行;而 .btn.is-loading 这种非 BEM 的 JS 钩子类,建议统一用 js- 前缀(如 js-loading),和 BEM 类名物理隔离。
- 媒体查询内不要重写 BEM 结构,比如在
@media (max-width: 768px)里再定义一套.header-mobile__logo,这等于放弃 BEM 的一致性 - SVG 内联图标用
时,别给加 BEM 类名——它不属于 DOM 组件树,加了也没用 - 服务端渲染(SSR)时,如果 HTML 已带 BEM 类名,客户端 JS 千万别用
document.querySelector('.btn')去找元素然后手动加--active,要用数据驱动更新类名,否则水合(hydration)会失败
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











