BEM类名越写越长反而更难维护,本质是混淆结构层级与语义层级,如.card__header__title违反Element不可嵌套原则,导致选择器膨胀、JS查询失效、重构风险高;应按复用性判断是否升为独立Block,并用stylelint等工具链强制校验。

为什么BEM类名越写越长,反而更难维护?
BEM本身不鼓励深层嵌套,但人容易把“结构层级”和“命名层级”混淆。比如看到 .card__header__title,以为是在还原 DOM 深度,其实它直接违反 BEM 原则:Element 不可嵌套。这类写法会让 CSS 选择器膨胀、JS 查询失效、重构时无法安全替换 block 名。
-
.card__header__title是非法的——正确应为.card__title(提升语义)或拆出新 block.header-title - 用
&__header { &__title {} }在 SCSS 中生成嵌套类,等于主动绕过 BEM 隔离,编译后破坏作用域边界 - 当一个组件内部有多个子模块(如
user-card里含avatar、badge),别硬塞进user-card__avatar__icon,而是让avatar成为独立 block
如何判断一个 class 是否该作为独立 Block 而非 Element?
关键看它是否具备「跨上下文复用」能力。如果某个视觉单元在别的页面或组件中也能原样出现(比如 avatar、status-badge、pill-tag),它就不是谁的子元素,而是一个 Block。
- 检查 Git blame:如果某段样式在多个文件里被复制粘贴,说明它本该是 Block
- 看 JS 使用方式:若
document.querySelector('.user-card__avatar')只在 user-card 内部调用,没问题;但若 elsewhere 也用querySelector('.user-card__avatar'),说明它已承担公共职责,应升为.avatar - 命名是否带上下文词:
user-card__avatar中的user-card是冗余前缀,真正语义是avatar;保留前缀只在防止命名冲突时必要(如项目已有第三方avatar)
SCSS 中哪些嵌套写法会悄悄破坏 BEM?
SCSS 的 & 符号很危险,空格、换行、缩进稍错,就产出后代选择器而非 BEM 类名。最常见的是误写成 &__item &__icon(带空格),结果编译为 .menu__item .menu__icon —— 这是典型的后代选择器,完全违背 BEM 封装前提。
- 只允许单层
&__xxx和&--xxx,禁止&__item { &__icon {} } - 禁用
@extend跨 block 复用,哪怕.button--small和.input--small样式相同,也必须各自定义 - 用
@use "sass:selector"+selector.unify()替代手写组合选择器,避免写出.card__header .card__title
工具链怎么卡住「伪BEM」写法?
靠人工 review 拦不住 .header_logo--dark 或 .card__body__text 这类错误。必须用 stylelint-selector-bem-pattern 在提交前拦截。
- 配置
"componentName": "[a-z][a-zA-Z0-9]+", "styleType": "bem",它能识别并报错:.card__body__text(嵌套 element)、.header_logo(单下划线)、.card--active--disabled(多修饰符) - CI 中运行
npx stylelint "**/*.{scss,css}" --fix,失败即阻断 PR 合并 - 配合目录约束:每个 Block 必须对应一个物理目录(如
/components/avatar/Avatar.scss),且该文件内所有类名必须以avatar开头,否则校验不通过
BEM 的冗余感往往来自把「DOM 结构」当「命名结构」,而不是按「语义边界」切分。真正难的不是写对 __ 和 --,而是每次加新 class 前,停下来问一句:这个东西,离开当前父容器还能不能独立存在?
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











