block名不能是header或btn,因二者不满足“功能闭环”前提:header语义随上下文变化,btn逻辑割裂,须拆解为nav-header、form-submit-button等具体功能块,且强制小写短横线命名。

为什么 block 名不能是 header 或 btn
因为 header 是 HTML 标签名,btn 是泛化缩写,两者都不满足“功能闭环”这一核心前提。一个真正的 block 必须能脱离当前页面结构独立存在、接收明确输入(如 search-form 接收关键词)、输出可预期行为(如触发搜索事件)。而 header 在导航栏、文章页、弹窗里语义完全不同;btn 在表单提交、模态框取消、侧边栏操作中逻辑割裂,强行复用只会让 btn__icon 在不同上下文里渲染出完全不同的 DOM 和样式。
-
header应拆解为具体功能块:nav-header(主导航区头部)、article-header(内容区标题容器) -
btn必须绑定场景:form-submit-button、modal-close-button、sidebar-action-button - 所有 block 名强制小写 + 短横线,禁止驼峰(
userProfile❌)或下划线(user_profile❌)
如何从旧代码里识别出真正的 block
别看 class 名字,直接查复用性和行为一致性。比如散落在各处的 .user-card、.profile-card、.member-item,如果它们都包含头像+姓名+状态徽章+操作按钮,且点击行为一致(跳转用户页),那这就是一个隐含的 block,应统一收敛为 user-profile-card。
- 反例
.section-2:纯视觉分组,无交互、无数据输入,不是 block - 反例
.table-row:只是 DOM 层级描述,真正 block 是data-table,其内部用data-table__row - 反例单独出现的
.active:这是 modifier,必须挂靠,如tab--active或menu-item--active
block 名写错会引发哪些连锁反应
最典型的是样式域坍塌:card 被滥用于文章摘要、用户资料、广告位三类结构迥异的模块,结果 card__title 在不同地方需要不同行高、颜色、字体粗细,最终只能靠 !important 或后代选择器硬覆盖,BEM 的隔离性彻底失效。
-
card--featured在广告模块定义了宽高,在用户模块却没定义——开发者不敢删,技术债滚雪球 - 新人看到
card__content,无法判断它是富文本、JSON 预览还是空状态占位符 - JS 中通过
document.querySelector('.card__content')获取节点,一旦 block 名不唯一,查询结果不可控
怎么让 BEM 规范真正落地不流于形式
靠 Code Review 拦不住手滑,必须在编辑器保存、Git 提交、CI 构建三个环节设防。用 stylelint-selector-bem-pattern 插件配置校验规则,自动报错 .header .logo 或 .btn-primary 这类非 BEM 写法;Prettier 禁用 htmlWhitespaceSensitivity 宽松模式,防止格式化破坏 block__elem block__elem--mod 的空格结构;CI 中跑 npx stylelint "**/*.{css,scss}",失败即阻断合并。
- 禁止在 JS 中拼接类名:
className={`card__body ${isCollapsed ? 'card__body--collapsed' : ''}`}→ 改用cn('card__body', { 'card__body--collapsed': isCollapsed }) - CSS Modules 项目中,BEM 类名照写不误:
.button、.button__icon、.button--primary,哈希由构建工具处理,语义保留在源码中 - 文件命名必须与 block 名一致:
components/UserProfileCard/UserProfileCard.module.css里只允许出现user-profile-card开头的类
user-profile-card__avatar,而是每次加新 class 前,先问一句:“它属于哪个能独立运行、有明确定义输入输出的 block?”——这个判断一旦松动,整个 BEM 结构就从根上开始松动。前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











