bem不是命名格式,是结构契约:类名必须锁死归属、层级、状态三层语义,且dom结构与class严格一一对应;card__title必须是card直系子元素,禁止嵌套或祖先选择器,工具链须实时拦截违规。

不能靠“名字够长”或“用了__和--”来判断,真正起作用的是类名是否把组件归属、结构层级、状态变体这三层语义锁死在字符串里,且团队能用工具当场拦住违规写法。
查 DOM 结构和类名是否严格一一对应
BEM 不是命名格式,是结构契约。只要 class 属性里出现 card__title,它就必须是 card 的直接子元素;出现 button--loading,就一定得同时存在 button。
- 错误示例:
<div class="card"><section><h3 class="card__title">标题</h3></section></div>——card__title不是card的直系子节点,BEM 结构断裂 - 错误示例:
class="card__content__title"—— BEM 禁止 element 嵌套 element,应拆成新 block 或改用card__content-title - 调试时打开开发者工具,搜
card__title,再看它父节点是不是card;不是,说明命名脱离了真实结构,后期维护必然翻车
看 CSS 里有没有空格选择器或依赖祖先的选择器
BEM 的核心价值之一是切断对 DOM 层级的隐式依赖。一旦 CSS 中出现 .card .title 或 .user-card > .avatar,就等于放弃 BEM 的作用域保障。
- 禁止写:
.card__title { font-size: 1.2em; }+.card--featured .card__title { color: #007bff; }—— 后者用祖先选择器破坏了card__title的独立性 - 正确写法:所有状态都靠 modifier 显式表达,比如
.card__title--featured,样式只绑定单个类名 - 用 stylelint 配置
selector-max-compound-selectors: 1,直接禁用含空格的选择器,比人工 Review 更可靠
检查 Modifier 是否可预测、可开关、不混入样式细节
修饰符不是“补丁区”,而是明确的状态标识。button--disabled 必须只响应禁用态,不能顺手在里面加 opacity: 0.6 或 cursor: not-allowed —— 后者属于基础 disabled 行为,前者属于视觉降级策略,该由设计系统统一定义。
- 危险信号:
button--primary--large--loading—— 三个 modifier 堆砌,说明组件职责过重,该拆 - 语义漂移常见于:
card--elevated本该只控制box-shadow,结果有人在里面加了padding和border-radius,导致后续想单独调整圆角必须覆盖 - 验证方式:删掉这个 modifier 类,页面是否仍能正常交互?如果 UI 错乱或功能异常,说明它绑定了不该管的东西
确认 CI 流程是否真正在卡住非 BEM 写法
没配 stylelint-selector-bem-pattern 插件,或者配了但没进 CI,等于没用。人总会漏、会赶时间、会抄错下划线数量。
- 必须启用插件并严格配置:
componentName: "[a-z][a-zA-Z0-9]+",拒绝大驼峰(UserCard)和下划线开头(_header) -
ignoreSelectors加上["/^\..+:(hover|focus|active|disabled)/"],避免误报伪类 - CI 脚本里跑
stylelint --config .stylelintrc.js src/**/*.css,不通过就阻断合并 —— 卡不住,规范就是废纸 - 最常被忽略的一点:
button--loading在 JS 里动态拼接时漏了基础类button,这种错误只有运行时才暴露,但 stylelint 能通过 AST 分析提前发现
真正难的不是写对一个 search-form__submit--disabled,而是在新增 20 个组件、50 个状态、3 个主题变体时,没人能靠记忆守住边界。工具链能不能在敲下回车前就报错,决定了 BEM 是加速器还是新包袱。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











