每个BEM block必须独占一个物理目录,因平铺文件会导致全局搜索困难、import顺序引发样式覆盖、元素归属混淆三类硬伤;且modifier须用&声明以保障语义上下文与样式继承。

每个 BEM block 必须独占一个物理目录,目录名即块名(全小写、中划线分隔),且该目录下只允许存放与该 block 严格对应的文件。
为什么不能把所有 block 都塞进 components/ 平级目录?
平铺 _button.scss、_card.scss 这类文件看似简单,实际会引发三类硬伤:
- 修改
.button--disabled时,得全局搜索是否在_theme.scss或_global.scss里被重复定义过 -
@import "components/_button"和@import "components/_reset"顺序一错,.button--primary的背景色可能被泛义规则(如[class*="--"])意外覆盖 - 新人看到
_card-button.scss会困惑:这是独立 block?还是card的元素?——其实它根本不该存在,.card__button必须写在_card.scss里
components/ 目录下每个 block 的文件结构必须严格对齐
以 button 为例,合法结构只有一种:
components/
└── button/
├── button.css // 或 button.scss,仅含 .button、.button__icon、.button--primary
├── button.tsx // React 组件,只导出 button 相关逻辑
└── button.test.tsx // 测试文件,不引用其他 block 的样式或组件
常见错误包括:
立即学习“前端免费学习笔记(深入)”;
- 在
button/目录里放icon.css——button__icon是元素,不是独立 block - 把
pagination.css放进user-list/目录 —— 它是可复用的独立 block,必须有自己的pagination/目录 - 用
Button.tsx(大驼峰)命名文件 —— 块名必须全小写、中划线,否则和button.css不一致,CI 会报错
Modifier 必须在 block 文件内用 & 声明,不能写成独立选择器
SCSS 中写法错误会导致样式断裂,尤其在多人协作或动态 class 注入时:
// ❌ 错误:脱离上下文,无法继承 block 的 specificity 或变量作用域
.button--primary {
background-color: #007bff;
}
// ✅ 正确:Modifier 必须基于 block,用 & 连接
.button {
padding: 0.5em 1em;
&--primary {
background-color: #007bff;
}
&__icon {
margin-right: 0.25em;
}
}
这种写法强制约束了三点:
-
.button--primary只能出现在.button的语义上下文中 - 修改
.button的font-size时,&--primary自动继承,无需重复声明 - 工具链(如
stylelint-selector-bem-pattern)能校验.button--primary是否漏写了.button父类
跨 block 样式污染比想象中更隐蔽
BEM 的物理隔离不是为了“看起来整洁”,而是阻断 CSS cascade 的隐式依赖。比如:
- 你改了
search-bar.css里的.search-bar__input边框,结果user-list__search-input也变了 —— 因为有人在user-list/里写了.search-bar__input这种跨块选择器 -
pagination.css里用了.button类名(而非.pagination__button),导致全局按钮样式被意外覆盖 - 有人在
pages/home.css里写.card .button—— 这违反 BEM 原则,必须用.card__button显式声明
真正难防的不是手误,而是当 block 数量超过 30 个后,没人记得清哪个文件里埋着一句 .button { ... }。目录物理隔离 + 单文件单 block + & 修饰符绑定,这三者缺一不可。


















