Block 和 Element 的根本区别在于能否独立存在:能独立复用、有完整逻辑的为 Block(如 .icon、.nav),否则为 Element;Element 必须直属于 Block,不可嵌套,命名需体现业务语义而非视觉或状态。

Block 和 Element 的根本区别不在写法,而在“能不能独立存在”
一个类名该是 .button 还是 .card__title,不取决于它在 DOM 里嵌得多深、长得有多小,而取决于它抽出来后还能不能自己用。能:就是 Block;不能:才是 Element。
比如 .icon 出现在按钮、卡片、弹窗、侧边栏多个地方,有自己的一套样式、尺寸、状态逻辑(.icon--loading、.icon--success),那它必须是 Block;硬写成 .button__icon 或 .card__icon,等于把它锁死在父组件里,后续复用、测试、维护全要绕弯。
常见误判:.header__nav —— 实际上 .nav 本身已是成熟 Block,有自己的 JS 行为和响应式逻辑,不该依附于 .header 存在;.header__nav 应直接写成 .nav,在 HTML 中嵌入即可。
Element 必须是 Block 的直接子元素,且不可再嵌套
BEM 不允许 .block__element__subpart 这种三层结构。一旦你写出 .card__title__icon,说明设计上已经出问题:要么这个图标其实是独立语义单元(该升为 .icon Block),要么它只是标题的视觉变体(该用修饰符,如 .card__title--with-icon)。
立即学习“前端免费学习笔记(深入)”;
验证方式很简单:打开浏览器开发者工具,定位到带 card__title 的标签,看它的父节点 class 是不是 card。如果不是——中间隔了 card__body 或 card__wrapper,那这个 card__title 就违反了 Element 必须直属于 Block 的规则。
-
.card > .card__title✅ 合规 -
.card > .card__body > .card__title❌ 违规:.card__title 实际属于 .card__body,应重命名为 .card__body__title 或(更合理)把 .card__body 升为 Block
命名冲突时,优先保 Block 的业务语义,而非视觉结构
设计稿里两个输入框长得像,但一个是搜索入口,一个是筛选条件,它们不该共用 .form__input。因为语义不同、行为不同(搜索要 debounce,筛选可能要联动)、未来变体也不同(.search-input--suggestion vs .filter-input--multi)。
正确做法是定义两个 Block:.search-input 和 .filter-input,各自管理自己的 Element(.search-input__icon、.filter-input__clear)。这样改样式、加逻辑、写测试都互不干扰。
反例:.primary-button 这种名字本身就是错的——Block 名里塞状态,等于把修饰符提前固化进名字,后续想加 .secondary-button 或 .button--ghost 就会混乱。它该是 .button + .button--primary。
真正难的不是双下划线怎么写,而是每次加类名前问一句:“它有没有资格独立存在?”
这个问题答错一次,后面就会多一堆补丁:临时加个 wrapper 类、用标签选择器强行覆盖、靠 !important 续命。BEM 的约束力不在语法,而在迫使你在写 CSS 前先理清组件边界。
尤其要注意那些“看起来只是容器”的类:比如 .section-2、.block-wrapper、.content-inner——它们没业务含义、没复用场景、没独立样式逻辑,就不该成为 Block;删掉类名,用语义化标签(<section>、<main>)或继承处理更轻量。


















