BEM 规范中状态修饰符必须用双连字符(如 btn--disabled),禁用单连字符、下划线或变量映射;应独立切换 classList,避免字符串拼接;CSS 规则需基于基础类解耦编写,禁用组合选择器和属性伪类。

用 btn--disabled 这类双连字符修饰符定义状态,别用 btn-disabled
BEM 规范里,-- 是修饰符的唯一合法分隔符。写成 btn--loading,工具链(比如 PurgeCSS)能识别这是 btn 块的临时状态;写成 btn-loading 就会被当成一个全新组件名,可能被误删,DevTools 里也看不出它和 btn 的语义关系。
常见错误包括:
- 用下划线
btn_loading:BEM 没约定含义,自动化工具不认 - 用单连字符
btn-disabled:解析为独立块,破坏层级可维护性 - 把状态塞进变量名,比如
isBtnDisabled:JS 里好命名,但 CSS 里无法直接映射到 class 列表
用 classList.toggle() 切换多个类,别拼字符串
手动拼接 el.className = 'btn btn--primary' + (isDisabled ? ' btn--disabled' : '') 容易漏空格、覆盖已有类、SSR hydration 时出错。而 classList.toggle() 是原子操作,浏览器原生优化,且天然支持“开关”语义。
正确做法是:
立即学习“前端免费学习笔记(深入)”;
- 每个状态对应一个独立 class,如
btn--disabled、btn--loading、btn--error - 切换时分别调用
el.classList.toggle('btn--disabled'),不耦合逻辑 - 避免在 JS 中维护 class 字符串状态,让 DOM 自己记
多个状态共存时,CSS 规则必须彼此解耦
当元素同时有 btn--disabled 和 btn--loading,样式不能写成 .btn--disabled.btn--loading { ... }。这种组合选择器会爆炸式增长,漏一个 class 就失效,且难以调试。
应该这样写:
.btn.btn--disabled { opacity: 0.6; cursor: not-allowed; }.btn.btn--loading { background: #eee; cursor: wait; }.btn.btn--error { border-color: #f44; }
所有规则都基于 .btn 基础类,各自只管自己那部分视觉变化,叠加效果由层叠顺序自然决定。
伪类和属性选择器不适合做组件状态控制
别用 [disabled] 或 :disabled 来驱动按钮禁用样式。这类选择器依赖 HTML 属性或原生语义,但组件状态往往比 DOM 属性更复杂——比如“正在提交中但还没禁用表单”、“加载失败但按钮仍可点”。
更关键的是:
- 属性选择器权重高(10),容易覆盖其他类规则,调试困难
- 无法表达中间态(如
btn--submitting),也不支持自定义状态名 - 服务端渲染(SSR)或 hydration 阶段,DOM 属性可能还没同步,导致闪屏
真正要盯住的,是 class 列表是否完整、CSS 规则是否彼此正交、以及层叠顺序是否符合预期——这三点漏一个,多状态切换就容易卡在某个中间态不动。


















