block是能脱离父容器独立存在、有完整样式/行为/测试且跨上下文复用的组件,如avatar;element必须依附block、无独立性、且须为block直接子节点,如card__title。

删掉父级后它还能不能跑起来
这是最硬的检验方式,不靠感觉、不看嵌套深浅,只看工程事实。一个节点如果删掉它所在的父容器后仍能正常渲染、有样式、有交互、能单独测试,那它就是 Block;否则就是 Element。
常见误判点:
- 把
card__header当成独立块——其实它没自己的 CSS 文件,也没独立 JS 行为,删掉card标签后直接失效 - 把
user-avatar写成user-card__avatar——但它在评论区、个人页、设置页都复用同一套逻辑和样式,理应是独立avatar块 - 看到 DOM 里有
<div class="card"><div class="card__inner"> <h2 class="card__title"> 就认定 <code>card__title是card__inner的子元素——错,card__title必须是card的直接子节点,否则违反 BEM 结构约束它有没有自己的 CSS/JS/测试文件
Block 在工程层面应具备封装完整性:有自己的样式文件(如
avatar.css)、可选的行为逻辑(如avatar.js)、能被 Storybook 单独预览、在单元测试中可独立挂载断言。而 Element 没有这些——它的样式定义在
card.css里,行为由card.js统一管理,测试时不会单独 rendercard__title。实操建议:
- 检查项目目录结构:如果某个类名对应一个独立文件夹(
/src/components/avatar/),大概率是 Block - 搜索代码库:
import ... from './avatar'存在,但找不到import ... from './card__title',后者就不可能是 Block - 在浏览器控制台执行
document.querySelector('.card__title').parentElement.classList,确认是否为card—— 不是?那命名或结构已违规
它出现在几个无关上下文中
复用性不是“可能用”,而是“已经用”。一个组件若只在卡片里出现一次,哪怕视觉上再像模块,它也只是
Element;只有在至少两个语义无关的模块中稳定复用(比如search-form同时用于首页顶部和弹窗内),才满足 Block 的基本门槛。注意边界:
-
section-2、block-wrapper这类纯视觉分组不算——它们没业务语义,只是临时容器 -
button__icon是否该升为icon块?看它有没有变体:icon--size-l、icon--color-error、是否在输入框/导航栏/卡片里都出现过 -
modal-close-button和list-delete-button外观不同、状态逻辑不同,不应共用button--delete,而应各自作为 Block
基础类名能不能省略
BEM 要求所有 Element 类名必须带完整块前缀,且不可省略。写
card__title是合法的,写title或card-title都破坏命名空间隔离。容易踩的坑:
- SCSS 中误用嵌套:
.card { &__content { &__title {} } }生成card__content__title—— 语法合法,语义非法,BEM 明确禁止元素嵌套元素 - 以为 “小” 就等于 Element:一个
chip组件体积小,但支持chip--outline、chip--disabled、在筛选器和标签云里都用,它就是 Block - 修饰符单独使用:
card__title--large必须和card__title共存,否则失去上下文锚点,CSS 规则可能被误匹配
真正难的不是命名规则本身,而是每次加新类名前,得停下来问一句:这个东西,到底有没有“自己活”的能力。
- 检查项目目录结构:如果某个类名对应一个独立文件夹(











