block名不能是“header”或“btn”,因为它们是html标签或泛化概念,缺乏功能闭环、独立存在性、明确输入输出及语义一致性,易导致bem结构混乱与维护困难。

为什么 block 名不能是 “header” 或 “btn”
因为 header 和 btn 是 HTML 标签名或通用概念,不是功能闭环的组件。它们缺乏明确边界和复用上下文:一个页面可能有多个 header(nav__header、article__header),而 btn 在表单里是提交动作,在弹窗里可能是取消操作——语义完全错位。
真正合格的 block 必须满足三个条件:
- 能独立存在:不依赖父容器 DOM 结构就能渲染并工作
- 有明确输入输出:比如
search-form接收关键词、触发搜索事件;notification-badge接收 count 值、响应点击跳转 - 命名不含位置/样式词:
left-sidebar、red-button都不合格,应改为sidebar-nav、button--primary
如何从旧项目中识别出真正的 block
别看 DOM 结构,先问“它是否被多处复用且行为一致”。比如旧代码里散落着 .user-card、.profile-card、.member-item,表面不同,但都包含头像+姓名+状态徽章+操作按钮——这就是一个隐含的 block,应统一为 user-profile-card。
反例也很典型:
-
.section-2:纯视觉分组,无功能逻辑,不该作为 block -
.table-row:只是 DOM 层级描述,不是可复用单元;真正 block 应是data-table,其内部data-table__row才合理 -
.active单独出现:这是 modifier,不是 block,必须挂靠在某个主体上(如tab--active)
block 名写错会导致什么连锁反应
一旦 block 名模糊或泛化,整个 BEM 结构就塌陷。比如定义了 card 作 block,但实际它既用于文章摘要、也用于用户资料、还用于广告位——三者结构差异大、样式互斥,结果就是:
-
card__title在不同场景下高度/颜色/行高全不一样,最后只能靠!important或后代选择器硬覆盖 -
card--featured在广告模块生效,在用户模块却没定义,开发者不敢删,技术债滚雪球 - 新人接手时看到
card__content,根本无法判断这个 content 是富文本?还是 JSON 数据预览?还是空状态占位符?
最隐蔽的坑是 block 名大小写混用:userProfile 看似合理,但和 JS 变量名冲突、grep 不方便、VS Code 插件无法识别——BEM 要求一律小写+短横线:user-profile。
React 组件中定义 block 名的实操建议
不要在 JSX 里动态拼接类名前缀,比如 className={`card__header ${isSticky ? 'card__header--sticky' : ''}`>。这会让 block 名散落在模板各处,一旦 rename 就漏改。
推荐做法是在组件顶部显式声明:
const BLOCK = 'user-profile';
然后所有类名基于它生成:
className={`${BLOCK}__avatar`}className={clsx(BLOCK, isEditing && `${BLOCK}--editing`)}- 搭配 TypeScript 类型约束,确保
BLOCK值只允许出现在预设白名单中
这样 block 名只在一个地方定义,改名、校验、搜索、IDE 补全全部可控。真正难的不是写对 __ 和 --,而是每次新增 class 时,先确认它归属哪个 block —— 这个判断本身,就是 BEM 的起点和终点。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











