bem通过块名、元素名、修饰符三层语义明确组件归属、结构和状态,使新人无需查源码即可理解样式作用域与意图,提升可维护性与协作效率。

因为BEM把组件归属、结构和状态直接编码进类名,新人不用猜、不翻源码、不问同事,打开文件就能定位作用域和意图。
看到 form-field__input--disabled 就知道它属于哪个模块、是什么元素、处于什么状态
传统命名如 input-disabled 或 disabled-input 无法回答“这个禁用态是给谁用的”——是表单字段?搜索框?还是弹窗里的输入框?BEM强制在类名里带出上下文:form-field 是块名(独立功能单元),__input 是其内部元素(不可单独存在),--disabled 是明确可触发的状态(由 JS 控制或 props 驱动)。这种三层语义让新人第一眼就建立准确心智模型,而不是靠试错或查 DOM 树反推。
- 块名必须语义唯一,
form-field比field或input-wrap更易识别边界 - 元素名禁止描述结构,
form-field__label合理,form-field__div-2是灾难——换标签就全崩 - 修饰符不能是视觉描述,
--blue不合法,--error或--compact才符合状态/变体定义
文件名与块名严格一一对应,ctrl+click 跳转即见全部样式逻辑
新人最怕“这个按钮样式到底在哪改?”——老项目常分散在 base.css、ui-kit.css、profile-page.css 里。BEM规定:所有 button 相关样式只能出现在 button.css 中,且只允许出现 button、button__icon、button--primary 这类类名。IDE 搜索文件名即得完整作用域,删组件时删文件即可,不会漏掉幽灵样式。
- 每个 BEM 块独占一个 CSS 文件,禁止跨文件定义同一 block 的样式
- 文件开头加注释说明用途和可用修饰符,例如
/* @modifiers: --small, --loading */ - 不依赖 HTML 结构隔离样式,所以 DOM 微调(比如把
<div class="card"> 换成 <code><article></article>)不影响样式生效修饰符绑定行为而非外观,
classList.toggle()时目标天然清晰新人最容易写的错是
user-card__avatar--small——--small是尺寸,不是状态,它没回答“什么时候该小”。BEM 要求修饰符必须关联可触发的行为或业务状态:user-card--compact(用户设置触发)、user-card__avatar--loading(JS 控制切换)。这样写 JS 时,el.classList.toggle('user-card__avatar--loading')的意图一目了然,不会写出btn-red-big-hover这种无法维护的组合名。- 禁止用修饰符表达位置关系,
nav__item--in-sidebar违反原则,应拆出新块sidebar-nav - 修饰符可叠加但需团队共识,
button--primary--loading是否合法、有无副作用,必须写进设计文档或 Code Review 清单 - 人工拼
my-button__icon--loading错误率高(漏下划线、大小写混),推荐用useNamespace类工具生成,错一个参数直接报错
真正难的不是记住
__和--,而是坚持每新增一个修饰符都同步更新文档、每改一个块都只动对应文件、每次动态加类都走预设修饰符列表——这些动作本身就在训练模块边界意识,而意识比语法重要得多。 - 禁止用修饰符表达位置关系,











