BEM不是一套要“用全”的规范,而是一套靠约束边界来防止样式失控的协作协议;必须确保每个可复用UI单元有明确Block名、不跨Block复用Element、Modifier只表达可预测状态。

直接说结论:BEM不是一套要“用全”的规范,而是一套靠约束边界来防止样式失控的协作协议。你不需要给每个 div 都套 block__element--modifier,但必须确保每个可复用的 UI 单元有明确的 Block 名、不跨 Block 复用 Element、Modifier 只表达可预测的状态。
如何判断一个组件该不该定义为独立 Block
Block 的核心是“能独立存在、被复用、被测试”。它不是容器,也不是布局词。
-
card、search-form、user-avatar是合法 Block:职责清晰,脱离上下文也能理解用途 -
container、section、wrapper不是 Block:它们不描述功能,只描述布局角色,后续加__或--会丢失语义上下文 - Vue/React 组件文件夹名 ≈ Block 名:比如
DocumentItem.vue对应listItem,而不是强行挂靠到父级documentListing - 如果某个“子结构”需要自己控制状态(比如
card__stats里还要分stats__value和stats__label),那它大概率该拆成新 Block:stat-badge
Element 命名最容易踩的三个坑
Element 不是 DOM 深度标记,而是逻辑归属声明。哪怕它在 HTML 里隔着四层 div,只要属于那个 Block,就该叫 block__element。
- 禁止嵌套 Element:
card__header__title是错的——BEM 明确禁止双下划线嵌套;应改为card__title,并确保它语义上确实属于 card 块 - 不能省略 Block 前缀:
title或card-title都不行,必须是card__title;否则 CSS 优先级、加载顺序、外层 font-size 都可能悄悄覆盖它 - Element 名要语义化,不是结构描述:
card__close-button✅,card__el-2❌;三个月后没人记得 el-2 是啥,但 close-button 一看就懂
Modifier 什么时候该用、什么时候不该用
Modifier 是开关,不是属性拼接器。它回答的是“这个东西现在处于哪种可预期的状态”,而不是“我要给它加点红”。
立即学习“前端免费学习笔记(深入)”;
- 合理使用:
button--disabled、modal--expanded、form-field--error—— 它们对应明确、可测试、可预测的业务状态 - 错误写法:
button--red(颜色是表现,不是状态;应为button--primary或button--danger) - 更隐蔽的坑:
user-card--small(尺寸是响应式行为,不是固有状态;应配合媒体查询用user-card--compact,或直接上@media) - 修饰符永远作用于 Block 或 Element 类名本身:
card__title--large可以,但card--featured__title--large是非法结构,编译器不会报错,但人脑会卡住
工具链没配好,BEM 就只是自我安慰
BEM 类名写对了,不代表它真起作用。很多团队卡在“写了但没生效”,问题往往出在构建和协作环节。
- VS Code 里敲
button没补全提示?说明没配 stylelint-bem 或 postcss-bem 插件,手拼类名极易出错(比如Card__title大小写混用,在 Windows 下不报错,Linux 构建直接失败) - 新人写了
header__logo__icon被工具报错,但没人告诉他该拆成logo独立 Block —— 这不是规范问题,是培训断层 - CSS Modules 或 Styled Components 中,BEM 仍适用,但得改写法:用
styles['btn--disabled']替代styles.disabled,避免变量名破坏语义 - 第三方库(如 Ant Design)不强改类名,但在它外面包一层 Block 容器:
my-dashboard__ant-table-wrapper,隔离样式泄漏
真正难的不是记住 __ 和 -- 的用法,而是每次写类名前,停下来问一句:这个元素逻辑上属于哪个可复用单元?它的状态是否真的需要显式声明?有没有人在半年后还能靠这个名字一眼看懂它该干什么?


















