BEM 的 block 应按可复用边界而非视觉切分,如“带搜索的用户列表页”需拆为 user-list、search-bar、pagination 三个独立 block;每个 block 必须对应独立文件路径且仅含自身类名,modifier 分 --(静态变体)和 is-(运行时状态),SCSS 中禁用空格嵌套,统一用 &__ 和 &--。

大型项目里,BEM的block不是按视觉切分,而是按可复用边界切分
一个“带搜索的用户列表页”不能笼统定义为 user-list-page block。它至少应拆成 user-list、search-bar、pagination 三个独立 block——因为它们各自能脱离当前页面单独存在、被 Storybook 演示、被其他路由复用。
常见错误现象:把整个页面容器当顶层 block,写一堆 page__header__logo__link 这类长名,结果改个 DOM 结构或抽个子组件,所有 CSS 都得重写。
- 检查标准很简单:删掉父级容器后,该区块能否在 Storybook 中独立渲染且逻辑完整?不能 → 它不该是 element,而该是 block
- 第三方组件(如
el-select)不强行套 BEM,而是用 wrapper block 包裹,例如form-field__select-wrapper,再通过form-field--required控制上下文语义 - 泛称类(
container、wrap、section-2)禁止作为 block 名,它们没业务含义,后期无法定位、无法搜索、无法替换
SCSS 中写 BEM,&__ 和 &-- 是安全的,空格嵌套是危险信号
SCSS 的嵌套语法容易让人误以为 .card { &__title { .icon { ... } } } 合理,但它编译出的是 .card__title .icon ——这是后代选择器,破坏了 BEM 的扁平作用域,一旦 .icon 被移到别处或复用,样式就失效。
正确写法只用 &__ 和 &--,不引入空格:
立即学习“前端免费学习笔记(深入)”;
.card {
&__title { font-size: 1.2rem; }
&__image { width: 100%; }
&--horizontal { display: flex; }
}- 真需控制子元素样式(比如
.card__content里的图标),优先用属性选择器:[data-icon="avatar"],而非.card__content__icon - 禁用
&__item &__icon这种带空格的写法,它等于隐式声明 DOM 层级依赖 - 多个 modifier 并列使用:
button button--primary button--disabled,而非button--primary--disabled(后者无法被工具链提取、不可读、不可组合)
文件结构必须和 BEM 分层对齐,否则命名再规范也白搭
BEM 不是字符串游戏,它是工程约束。每个 block 必须对应一个独立文件路径,且文件内只准出现以该 block 名开头的类。
比如 user-list block 的样式必须放在 src/components/user-list/user-list.css,里面只允许有 .user-list、.user-list__item、.user-list--loading 等类;若出现 .user-item 或 .list-item,就是违规。
- 用
stylelint-selector-bem-pattern自动校验,配置规则强制匹配^(?!(?:u-|is-))([a-z][a-z0-9]*)(?:-[a-z0-9]+)*(__[a-z][a-z0-9]*?(?:-[a-z0-9]+*)*)?(--[a-z][a-z0-9]*?(?:-[a-z0-9]+*)*)?$ - 布局类(
grid、flex-row)走 utility-first 路线,不参与 BEM 命名体系,避免和业务 block 混淆 - 主题变量(颜色、间距)抽成 CSS 自定义属性,如
--user-list-bg,modifier 只负责开关状态,不硬编码值(user-list--dark开启,而非user-list--bg-gray-800)
修饰符 is-* 和 --* 不是随便换着用,混用会直接导致 JS 控制混乱
--* 描述静态变体(button--primary、modal--fullscreen),由 HTML class 决定,构建时可预判;is-* 表达运行时状态(is-open、is-disabled),由 JS 动态增删,二者语义不同、生命周期不同、工具链处理方式也不同。
常见错误:给按钮同时加 button--disabled 和 is-disabled,结果 JS 切换 is-disabled 时,CSS 规则因权重或覆盖顺序失效,按钮看起来没反应。
- 统一约定:所有 UI 状态变更(展开/收起/加载中/校验失败)用
is-*,所有设计稿标注的“变体”(大号/小号/暗色模式/卡片式)用--* - 禁止
button__icon--loading is-loading这种冗余写法;button__icon--loading已足够表达意图,is-loading属于更高层级的 block 状态 - React/Vue 中,用
classnames或clsx统一管理 modifier 注入点,但 block 名必须在组件顶层常量定义(const BLOCK = 'user-card'),避免拼写不一致
BEM 在大型项目里最易被忽略的一点:它不解决“怎么写样式”,而是锁定“谁该管哪段样式”。一旦 block 边界模糊、文件路径错位、modifier 语义漂移,后面所有自动化工具(stylelint、postcss-bem-linter、Storybook 自动分类)都会失效——问题不是出在命名上,是出在协作契约没对齐。


















