BEM变体存在是为适配现代前端约束:React组件粒度、CSS-in-JS、构建体积与团队习惯;双连字符(--)语义清晰且支持SCSS嵌套,单连字符(-)压缩更优但易混淆;UI库加前缀(如el-button)防污染并支持多版本共存,业务组件通常无需。
.block__element--modifier
BEM 原始规范由 Yandex 提出,但实际项目中几乎没人 100% 照搬。根本原因是:原生 BEM 对长类名容忍度高、对工具链无要求,而现代前端要兼顾 React 组件粒度、CSS-in-JS、构建产物体积、可读性与团队习惯。变体不是“不守规矩”,而是为解决具体约束做的权衡。
double-dash 和 single-dash 修饰符写法怎么选标准写法是 .block--modifier(双连字符),但有些团队改用 .block-modifier(单连字符)。这不是随意缩写,背后有明确取舍:
--:语义清晰,和 __ 形成视觉对称,CSS 预处理器(如 SCSS)能安全嵌套 &--hover;但类名更长,压缩后仍明显-:缩短类名(.btn-primary 比 .btn--primary 少 2 字节),适合对 bundle size 敏感的项目;但容易和 Block 名中的连字符混淆,比如 .user-profile-active 无法判断 profile 是 Block 还是 Element.btn--large 和 .btn-primary,会让开发者反复查文档确认规则el-button)适用什么场景像 Element Plus、Ant Design 这类 UI 库普遍采用 [prefix]-[block] 格式(如 el-button、ant-table),这不是 BEM 的“错误用法”,而是封装隔离的刚需:
button 太通用,el-button 明确归属el-button 和 el-button-v2 可同时加载,而 .button--v2 会和旧版样式冲突user-card 加 my-user-card 前缀反而增加认知负担,除非跨团队共享组件库classnames + BEM 的常见错配点很多人以为装了 classnames 就算用了 BEM,其实关键在 Block 名是否收敛:
立即学习“前端免费学习笔记(深入)”;
className={cx('card__header', { 'card__header--expanded': expanded })} —— Block 名硬编码在 JSX 里,组件重命名或复用时类名全崩const BLOCK = 'user-profile',然后统一用 cx(`${BLOCK}__header`, { [`${BLOCK}__header--expanded`]: expanded })
useBem('user-profile') 返回 getBlock、getElement 方法,避免字符串拼接出错真正难的不是记住 __ 和 --,而是每次加一个新 class 时,得判断它该属于 Block、Element 还是 Modifier——这个决策一旦错,后续所有样式都可能变成“修修补补式维护”。