bem能避免样式污染和选择器冲突,因为类名自带作用域,如.user-card__avatar与.product-card__avatar天然隔离,不依赖dom结构;必须严格按block__element--modifier拼写,禁止跨块复用或漏写基础块类。

为什么BEM能避免样式污染和选择器冲突
因为类名自带作用域,.user-card__avatar 和 .product-card__avatar 天然隔离,哪怕都叫 avatar,也不会互相覆盖。浏览器只认完整类名,不靠父级关系推断归属——这直接砍掉了 .card .avatar 这种依赖 DOM 结构的选择器。
常见错误现象:两个子应用都引入了 .btn,结果 padding 覆盖、颜色错乱;用 BEM 后变成 checkout-btn__submit 和 profile-btn__cancel,冲突归零。
- 必须严格按
block__element--modifier拼写,不能漏掉基础块类(比如只写card--featured却没写card) - 禁止跨块复用元素类:
card__title不能出现在非card容器里 - 修饰符必须和对应块/元素共存:
button--disabled要和button一起用,否则构建工具无法识别上下文
为什么BEM选择器更快但容易被写坏
.header__nav-item--active 是单类名,浏览器查哈希表一次命中;而 .header .nav .item.active 要从所有 .item 元素开始,逐层往上找父级是否匹配 .header,DOM 深了就明显卡顿。
但 Sass 里一不小心就写崩:
-
.header { &__nav { } }→ 编译出安全的.header__nav -
.header { & .header__logo { } }→ 编译出带空格的.header .header__logo,性能退化、语义断裂 - DevTools → Elements → Computed → Styles 面板里,一眼就能看出有没有空格——有空格就是隐患
- 构建后用
grep -r "\.[a-z]\+ \.[a-z]" dist/快速扫出残留嵌套选择器
BEM名字长,但真正让CSS变胖的是修饰符失控
BEM本身不增大体积,Spotify 实测落地后 CSS 体积下降 18%;真正爆炸的是修饰符滥用——比如为每个像素值、颜色码单独建类:button--bg-blue-500、button--padding-12。
一个 Block 的修饰符总数建议 ≤5 个,只用于明确、有限、可枚举的状态:
- ✅
button--primary、input--error、card--loading - ❌
button--width-200px、header--left(位置不是稳定状态)、btn(实现方式,非角色) - 响应式逻辑必须绑定到 BEM 类上,比如
product-card--stacked,而不是全局is-mobile类 - JS 中动态拼接类名(如
${block}__${elem}--${mod})会破坏 PurgeCSS 推断能力,导致未用样式无法剔除
CSS Modules比BEM少写类名但多了构建依赖
BEM 类名靠人脑维护唯一性,写多了一眼难辨,比如 .product-card__image-wrapper--hover-disabled;CSS Modules 用构建时哈希(如 Button_active_abc123)天然隔离,不用拼接、不怕重名。
但代价是:
- webpack 配置必须精准:只对
.module.css启用模块化,别把重置样式也模块化了 -
localIdentName别用默认值,推荐[name]__[local]--[hash:base64:5],兼顾可读与唯一 - TypeScript 项目要加
declare module '*.module.css',否则 import 报类型错误 - Vue 里
<style scoped></style>和<style module></style>不能混用,后者必须显式绑定:class="$style.xxx"
search-bar__input 必须严格在 search-bar 根元素内,跨了就不是 BEM,是伪规范。前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











