<p>BEM 通过 block-element-modifier 结构实现样式隔离,避免类名冲突。如 .user-cardavatar 与 .profile-cardavatar 互不干扰,因 block 名构成命名空间;必须严格使用 __ 和 -- 语法,禁用嵌套与手拼类名,确保工具链识别与封装性。</p>

直接用 .button 或 .header 这类泛化类名,项目一上规模必然炸锅——不是谁先加载的问题,而是浏览器根本不管“谁写的”,只按层叠顺序和权重匹配。BEM 不是靠名字更长防冲突,是把归属(block)、层级(element)、状态(modifier)全锁进类名里,让冲突从“随机覆盖”变成“结构错误,一眼可查”。
为什么 .user-card__avatar 和 .profile-card__avatar 不会互相覆盖
BEM 的 block 名(如 user-card)天然构成命名空间。两个不同 block 下的同名 element(__avatar)编译后就是完全不同的类名,浏览器不会匹配到同一个规则。.user-card__avatar 只响应 user-card 模块内定义的样式,哪怕 profile-card 也写了 __avatar,它们互不感知。
- 错误做法:
.avatar当通用类复用 → 改一处圆角,三处头像全变 - 错误写法:
.card .avatar→ DOM 加个 wrapper 就失效,且无法反推来源 - Element(
__avatar)不能脱离 Block 单独写样式,否则既违反 BEM 原则,又失去复用前提
__ 和 -- 是语法契约,不是风格偏好
__ 和 -- 是工具链识别 BEM 结构的硬性锚点。所有 lint 工具、VS Code 插件、CSS 提取逻辑都靠它做正则匹配,写错就等于关掉防护。
- ❌ 错误写法:
.card-header--large(漏一个_)、.button__icon_error(单_代替--)、.card__body--compact--dark(嵌套修饰符) - ✅ 正确做法:配置
stylelint-selector-bem-pattern,规则设为{"styleType": "bem"},CI 中跑npx stylelint "**/*.{css,scss}"阻断违规提交 - SCSS 嵌套写法
.user-card { .avatar { } }编译出.user-card .avatar,破坏封装性,必须平铺书写
JS 动态拼接类名时最容易踩的坑
手拼字符串看着快,实际在 CI 环境下极易因空格、连字符、大小写漏写而挂掉。本地开发跑得通,Linux 构建直接报错。
立即学习“前端免费学习笔记(深入)”;
- ❌ 危险写法:
className={`button button--${variant} ${hasIcon ? 'button__icon' : ''}`}(缺空格、button__icon脱离 block、无防错) - ✅ 推荐封装常量:
const BLOCK = 'search-form'; const cn = (e, m) => `${BLOCK}${e ? '__' + e : ''}${m ? '--' + m : ''}`,调用cn('input', 'disabled')得到search-form__input--disabled - 禁止在 JS 中拼接 BEM 类名字符串,统一用工具函数(如
clsx或自定义cn),避免大小写混用、空格遗漏、连字符错位
真正难的不是写对第一个 __,而是每次写 class 前都得想清楚:“它属于谁?会不会在别的上下文生效?”——BEM 的约束力不在语法,而在这个习惯是否成了肌肉记忆。


















